Skip to content

Commit 328aaae

Browse files
LAPLACE Phase5
1 parent 0d93062 commit 328aaae

11 files changed

Lines changed: 747 additions & 69 deletions

File tree

roadmap/laplace-pspice-feasibility.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ This approach keeps the implementation small, aligns with existing source-genera
6868

6969
## Current Status
7070

71-
Canonical `E`-source and `G`-source `LAPLACE` support is implemented by SpiceSharpParser. `F`, `H`, `B`, alternate syntaxes, delay, and `M=` support remain deferred.
71+
`E`-source and `G`-source `LAPLACE` support is implemented by SpiceSharpParser for canonical assignment, no-equals expression-pair, and equals-after-keyword expression-pair syntax. `F`, `H`, `B`, function-like `VALUE={LAPLACE(...)}`, delay, and `M=` support remain deferred.
7272

7373
Phase 1 grammar groundwork is implemented: expression-to-expression assignments such as `{V(in)} = {1/(1+s*tau)}` and `{V(in1,in2)} = {1/(1+s*tau)}` are preserved as `ExpressionAssignmentParameter`.
7474

@@ -78,6 +78,8 @@ Phase 3 `E`-source mapping is implemented: canonical `Ename out+ out- LAPLACE {V
7878

7979
Phase 4 `G`-source mapping is implemented: canonical `Gname out+ out- LAPLACE {V(ctrl)}` `= {H(s)}` and `V(ctrl+,ctrl-)` forms map to `LaplaceVoltageControlledCurrentSource`, preserve the existing `G` current-source sign convention, reject unsupported `M=` / delay options, and have OP / AC integration coverage.
8080

81+
Phase 5 syntax compatibility is implemented: `E` / `G` sources accept `LAPLACE {V(...)} = {H(s)}`, `LAPLACE {V(...)} {H(s)}`, and `LAPLACE = {V(...)} {H(s)}` as equivalent voltage-controlled forms, while known deferred `VALUE={LAPLACE(...)}` and `B`-source forms produce targeted reader diagnostics.
82+
8183
Adjacent features already exist:
8284

8385
- `VALUE={expr}` behavioral sources.
@@ -598,9 +600,9 @@ The last recognizer is deliberate. If a user writes a known PSpice Laplace varia
598600

599601
| Family | Example | Near-term behavior | Notes |
600602
|--------|---------|--------------------|-------|
601-
| Canonical assignment | `E1 out 0 LAPLACE {V(in)} = {1/(1+s*tau)}` | Support first | Exercises grammar gap and runtime mapping. |
602-
| No-equals expression pair | `E1 out 0 LAPLACE {V(in)} {1/(1+s*tau)}` | Add after canonical | Should normalize to the same definition and coefficients. |
603-
| Equals-after-keyword pair | `E1 out 0 LAPLACE = {V(in)} {1/(1+s*tau)}` | Add after canonical | Likely parses differently; keep syntax handling isolated in a recognizer. |
603+
| Canonical assignment | `E1 out 0 LAPLACE {V(in)} = {1/(1+s*tau)}` | Supported | Exercises grammar gap and runtime mapping. |
604+
| No-equals expression pair | `E1 out 0 LAPLACE {V(in)} {1/(1+s*tau)}` | Supported | Normalizes to the same definition and coefficients. |
605+
| Equals-after-keyword pair | `E1 out 0 LAPLACE = {V(in)} {1/(1+s*tau)}` | Supported | Parses differently; syntax handling stays isolated in a recognizer. |
604606
| Current-controlled | `F1 out 0 LAPLACE {I(Vsense)} = {H(s)}` | Later milestone | Runtime entities exist, but controlling-source parsing and PSpice compatibility need tests. |
605607
| Function-like `VALUE` | `E1 out 0 VALUE = {LAPLACE(V(in), H(s))}` | Investigate later | Must be handled as source-level Laplace, not a scalar expression function. |
606608
| `B` source ABM | `B1 out 0 V = {LAPLACE(V(in), H(s))}` | Investigate later | May require arbitrary input-expression lowering or custom behavior. |
@@ -676,6 +678,8 @@ Status: implemented for canonical `G` source syntax. `M=`, delay options, `F`, a
676678

677679
### Phase 5: Syntax Compatibility Layer
678680

681+
Status: implemented for `E` / `G` no-equals and equals-after-keyword voltage-controlled syntax. `VALUE={LAPLACE(...)}`, `B`, `F`, `H`, `M=`, and delay options remain diagnostic-only / deferred.
682+
679683
1. Add `UnsupportedKnownVariantRecognizer` so common deferred forms receive targeted diagnostics.
680684
2. Add `NoEqualsExpressionPairRecognizer` for `LAPLACE {input} {transfer}` only after canonical tests pass.
681685
3. Add `EqualsExpressionPairRecognizer` for `LAPLACE = {input} {transfer}` only after no-equals tests pass.

src/SpiceSharpParser.IntegrationTests/AnalogBehavioralModeling/LaplaceTests.cs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,26 @@ public void When_ELaplaceLowPassUsesDifferentialInput_Expect_ControlNodeDifferen
8484
Assert.True(EqualsWithTol(1.5, export), $"Expected OP gain from differential input near 1.5, got {export}.");
8585
}
8686

87+
[Fact]
88+
public void When_ELaplaceNoEqualsSyntaxRunsOp_Expect_DcGainNearOne()
89+
{
90+
var model = GetSpiceSharpModel(
91+
"E LAPLACE no-equals OP",
92+
".PARAM tau=1u",
93+
"VIN in 0 1",
94+
"ELOW out 0 LAPLACE {V(in)} {1/(1+s*tau)}",
95+
"RLOAD out 0 1k",
96+
".OP",
97+
".SAVE V(out)",
98+
".END");
99+
100+
AssertNoValidationErrors(model);
101+
Assert.IsType<LaplaceVoltageControlledVoltageSource>(model.Circuit["ELOW"]);
102+
103+
double export = RunOpSimulation(model, "V(out)");
104+
Assert.True(EqualsWithTol(1.0, export), $"Expected OP gain near 1, got {export}.");
105+
}
106+
87107
[Fact]
88108
public void When_ELaplaceLowPassRunsAcAtCutoff_Expect_ExpectedMagnitudeAndPhase()
89109
{
@@ -184,6 +204,27 @@ public void When_GLaplaceLowPassUsesDifferentialInput_Expect_ControlNodeDifferen
184204
Assert.True(EqualsWithTol(-1.5, export), $"Expected OP load voltage from differential input near -1.5, got {export}.");
185205
}
186206

207+
[Fact]
208+
public void When_GLaplaceEqualsAfterKeywordSyntaxRunsOp_Expect_LoadVoltageWithCurrentSourceSign()
209+
{
210+
var model = GetSpiceSharpModel(
211+
"G LAPLACE equals-after-keyword OP",
212+
".PARAM gm=1m",
213+
".PARAM tau=1u",
214+
"VIN in 0 1",
215+
"GLOW out 0 LAPLACE = {V(in)} {gm/(1+s*tau)}",
216+
"RLOAD out 0 1k",
217+
".OP",
218+
".SAVE V(out)",
219+
".END");
220+
221+
AssertNoValidationErrors(model);
222+
Assert.IsType<LaplaceVoltageControlledCurrentSource>(model.Circuit["GLOW"]);
223+
224+
double export = RunOpSimulation(model, "V(out)");
225+
Assert.True(EqualsWithTol(-1.0, export), $"Expected OP load voltage near -1, got {export}.");
226+
}
227+
187228
[Fact]
188229
public void When_GLaplaceLowPassRunsAcAtCutoff_Expect_ExpectedMagnitudeAndPhase()
189230
{
@@ -319,6 +360,36 @@ public void When_GLaplaceUnsupportedMultiplierIsUsed_Expect_ReaderValidationErro
319360
AssertReaderErrorContains(model, "multiplier");
320361
}
321362

363+
[Fact]
364+
public void When_ValueLaplaceFunctionIsUsed_Expect_ReaderValidationError()
365+
{
366+
var model = GetSpiceSharpModel(
367+
"E VALUE LAPLACE unsupported",
368+
"VIN in 0 1",
369+
"EBAD out 0 VALUE = {LAPLACE(V(in), 1/(1+s))}",
370+
"RLOAD out 0 1k",
371+
".OP",
372+
".SAVE V(out)",
373+
".END");
374+
375+
AssertReaderErrorContains(model, "function syntax");
376+
}
377+
378+
[Fact]
379+
public void When_BVoltageLaplaceFunctionIsUsed_Expect_ReaderValidationError()
380+
{
381+
var model = GetSpiceSharpModel(
382+
"B LAPLACE unsupported",
383+
"VIN in 0 1",
384+
"BBAD out 0 V={LAPLACE(V(in), 1/(1+s))}",
385+
"RLOAD out 0 1k",
386+
".OP",
387+
".SAVE V(out)",
388+
".END");
389+
390+
AssertReaderErrorContains(model, "function syntax");
391+
}
392+
322393
[Fact]
323394
public void When_HLaplaceIsUsed_Expect_ReaderValidationError()
324395
{

0 commit comments

Comments
 (0)