Skip to content

Commit c54de43

Browse files
committed
fix algebrite. Tell user if site crashes
1 parent d32d234 commit c54de43

File tree

5 files changed

+97
-16
lines changed

5 files changed

+97
-16
lines changed

src/App.jsx

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ function App() {
159159
const [schemHistory, setSchemHistory] = useState({ pointer: 0, state: [modifiedSchematic] });
160160
const [urlSnackbar, setUrlSnackbar] = useState(false);
161161
const [errorSnackbar, setErrorSnackbar] = useState(false);
162+
const [unsolveSnackbar, setUnsolveSnackbar] = useState(false);
162163

163164
const handleSnackbarClick = () => {
164165
window.location.href = window.location.origin + window.location.pathname;
@@ -219,11 +220,30 @@ function App() {
219220
);
220221
}
221222

223+
function SnackbarUnsolvable() {
224+
return (
225+
<Snackbar
226+
open={unsolveSnackbar}
227+
autoHideDuration={10000}
228+
onClose={() => setUnsolveSnackbar(false)}
229+
anchorOrigin={{ vertical: "top", horizontal: "right" }}
230+
message="This Snackbar will be dismissed in 5 seconds."
231+
>
232+
<SnackbarContent
233+
message="The circuit seems to be unsolvable. Please double check your connections and try using Sympy if not already. Check the console for more details, and feel free to leave a comment."
234+
sx={{
235+
backgroundColor: "#ec1186ff",
236+
color: "#fff",
237+
maxWidth: 200,
238+
}}
239+
/>
240+
</Snackbar>
241+
);
242+
}
243+
222244
const componentValuesSolved = {};
223245
for (const key in componentValues) componentValuesSolved[key] = componentValues[key].value * units[componentValues[key].type][componentValues[key].unit];
224246

225-
// const { freq_new, mag_new } = new_calculate_tf(results.complexResponse, fRange, settings.resolution, componentValuesSolved, errorSnackbar, () => setErrorSnackbar(true));
226-
227247
const [freq_new, setFreqNew] = useState(null);
228248
const [mag_new, setMagNew] = useState(null);
229249
useEffect(() => {
@@ -275,6 +295,7 @@ function App() {
275295
<>
276296
<LetUserKnowAboutURL />
277297
<SnackbarError />
298+
<SnackbarUnsolvable />
278299
<NavBar stateToURL={stateToURL} />
279300
<div className="w-100 p-2 bg-green pb-5" key="wrapper">
280301
{/* <div className="container-xl" key="topContainer"> */}
@@ -303,7 +324,13 @@ function App() {
303324
</div>
304325
</div>
305326
<div className="row shadow-sm rounded bg-lightgreen my-2 py-3" id="schematic">
306-
<ChoseTF setResults={setResults} nodes={nodes} fullyConnectedComponents={fullyConnectedComponents} componentValuesSolved={componentValuesSolved} />
327+
<ChoseTF
328+
setResults={setResults}
329+
nodes={nodes}
330+
fullyConnectedComponents={fullyConnectedComponents}
331+
componentValuesSolved={componentValuesSolved}
332+
setUnsolveSnackbar={setUnsolveSnackbar}
333+
/>
307334
{results.text != "" && (
308335
<>
309336
<DisplayMathML title="Laplace Transform" textResult={results.text} mathML={results.mathML} caclDone={results.text != ""} />

src/ChoseTF.jsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ function formatMathML(mathml, p, drivers) {
2626
return `<math><mfrac><mrow><mi>${p}</mi></mrow><mrow><msub><mi>${drivers[0] == "vin" ? "V" : "I"}</mi><mi>in</mi></msub></mrow></mfrac><mo>=</mo>${mathml}</math>`;
2727
}
2828

29-
export function ChoseTF({ setResults, nodes, fullyConnectedComponents, componentValuesSolved }) {
29+
export function ChoseTF({ setResults, nodes, fullyConnectedComponents, componentValuesSolved, setUnsolveSnackbar }) {
3030
const [algebraic, setAlgebraic] = useState("algebrite");
3131
const [loading, setLoading] = useState(false);
3232
const [loadedPyo, setLoadedPyo] = useState(null);
@@ -87,6 +87,12 @@ export function ChoseTF({ setResults, nodes, fullyConnectedComponents, component
8787
loadedPyo,
8888
algebraic,
8989
);
90+
if (textResult === "" && mathml === "" && complex_response === "") {
91+
setUnsolveSnackbar((x) => {
92+
if (!x) return true;
93+
else return x;
94+
});
95+
}
9096
const editedMathMl = formatMathML(mathml, p, drivers);
9197
const editedMathMlNumeric = formatMathML(numericResult, p, drivers);
9298
setResults({

src/new_solveMNA.js

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,16 @@ rx = Abs(result_numeric_complex)
3737
3838
str(result_simplified), mathml(result_simplified, printer='presentation'), str(rx), mathml(result_numeric_simplified, printer='presentation'), str(result_numeric_simplified)
3939
`;
40-
const [textResult, mathml, complex_response, numericML, numericText] = await pyodide.runPythonAsync(sympyString);
41-
const newNumeric = numericML;
42-
const newNumericText = numericText.replaceAll("**", "^");
40+
try {
41+
const [textResult, mathml, complex_response, numericML, numericText] = await pyodide.runPythonAsync(sympyString);
42+
const newNumeric = numericML;
43+
const newNumericText = numericText.replaceAll("**", "^");
4344

44-
return [textResult, removeFenced(mathml), complex_response, removeFenced(newNumeric), newNumericText];
45+
return [textResult, removeFenced(mathml), complex_response, removeFenced(newNumeric), newNumericText];
46+
} catch (err) {
47+
console.log("Solving MNA matrix failed with this error:", err);
48+
return ["", "", "", "", ""];
49+
}
4550
}
4651

4752
async function solveWithAlgebrite(matrixStr, mnaMatrix, resIndex, resIndex2) {
@@ -72,7 +77,6 @@ async function solveWithAlgebrite(matrixStr, mnaMatrix, resIndex, resIndex2) {
7277
return [textResult, mathml, complex_response];
7378
} catch (err) {
7479
console.log("Building MNA matrix failed with this error:", err);
75-
//FIXME - show the toast
7680
return ["", "", ""];
7781
}
7882
}
@@ -203,7 +207,7 @@ export async function build_and_solve_mna(numNodes, chosenPlot, fullyConnectedCo
203207
var numericResult, textResult, mathml, complex_response, numericText;
204208

205209
if (solver === "algebrite") {
206-
[textResult, mathml, complex_response] = solveWithAlgebrite(nerdStr, mnaMatrix, resIndex, resIndex2);
210+
[textResult, mathml, complex_response] = await solveWithAlgebrite(nerdStr, mnaMatrix, resIndex, resIndex2);
207211
} else {
208212
[textResult, mathml, complex_response, numericResult, numericText] = await solveWithSymPy(nerdStr, mnaMatrix, elementMap, resIndex, resIndex2, componentValuesSolved, pyodide);
209213
}

tests/circuit.test.js

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,44 @@ test("voltage in current probe - 1", async () => {
3636
const [textResult, _mathml, complex_response, _numericResult, numericText] = await build_and_solve_mna(3, ["Y0"], components, values, pyodide, "sympy");
3737
expect(textResult).toEqual("C0*L0*s**2/(C0*L0*R0*s**2 + L0*s + R0)");
3838
// expect(_mathml).toEqual(null);
39-
expect(complex_response).toEqual("C0*L0*s**2/sqrt(C0**2*L0**2*R0**2*s**4 - 2*C0*L0*R0**2*s**2 + L0**2*s**2 + R0**2)");
39+
expect(complex_response).toEqual("1.0e-24*s**2/sqrt(1.0e-40*s**4 - 1.0e-20*s**2 + 1)");
4040
expect(numericText).toEqual("1.0e-20*s^2/(1.0e-16*s^2 + 1.0e-6*s + 10000)");
4141
});
4242

43+
test("voltage in current probe (algebrite) - 1", async () => {
44+
const components = {
45+
Y0: {
46+
ports: [0, 1],
47+
type: "iprobe",
48+
},
49+
vin: {
50+
ports: [0],
51+
type: "vin",
52+
},
53+
L0: {
54+
ports: [0, 2],
55+
type: "inductor",
56+
},
57+
R0: {
58+
ports: [1, 2],
59+
type: "resistor",
60+
},
61+
C0: {
62+
ports: [2, null],
63+
type: "capacitor",
64+
},
65+
};
66+
const values = {
67+
L0: 0.000001,
68+
R0: 10000,
69+
C0: 1.0000000000000002e-14,
70+
};
71+
const [textResult, _mathml, complex_response] = await build_and_solve_mna(3, ["Y0"], components, values, null, "algebrite");
72+
expect(textResult).toEqual("C0*L0*s^2/(R0+C0*L0*R0*s^2+L0*s)");
73+
// expect(_mathml).toEqual(null);
74+
expect(complex_response).toEqual("abs(C0)*abs(s)/((1-2*C0*R0^2/L0+C0^2*R0^2*s^2+R0^2/(L0^2*s^2))^(1/2))");
75+
});
76+
4377
test("voltage in current probe - 2", async () => {
4478
const components = {
4579
R0: {
@@ -71,7 +105,7 @@ test("voltage in current probe - 2", async () => {
71105
const [textResult, _mathml, complex_response, _numericResult, numericText] = await build_and_solve_mna(3, ["Y0"], components, values, pyodide, "sympy");
72106
expect(textResult).toEqual("C0*L0*s**2/(C0*L0*R0*s**2 + L0*s + R0)");
73107
// expect(_mathml).toEqual(null);
74-
expect(complex_response).toEqual("C0*L0*s**2/sqrt(C0**2*L0**2*R0**2*s**4 - 2*C0*L0*R0**2*s**2 + L0**2*s**2 + R0**2)");
108+
expect(complex_response).toEqual("1.0e-24*s**2/sqrt(1.0e-40*s**4 - 1.0e-20*s**2 + 1)");
75109
expect(numericText).toEqual("1.0e-20*s^2/(1.0e-16*s^2 + 1.0e-6*s + 10000)");
76110
});
77111

@@ -106,7 +140,7 @@ test("current in current probe - 2", async () => {
106140
const [textResult, _mathml, complex_response, _numericResult, numericText] = await build_and_solve_mna(3, ["Y0"], components, values, pyodide, "sympy");
107141
expect(textResult).toEqual("L0*s/(L0*s + R0)");
108142
// expect(_mathml).toEqual(null);
109-
expect(complex_response).toEqual("L0*s/sqrt(L0**2*s**2 + R0**2)");
143+
expect(complex_response).toEqual("1.0e-10*s/sqrt(1.0e-20*s**2 + 1)");
110144
expect(numericText).toEqual("1.0e-6*s/(1.0e-6*s + 10000)");
111145
});
112146

@@ -136,7 +170,7 @@ test("current in current probe VCIS - 3", async () => {
136170
const [textResult, _mathml, complex_response, _numericResult, numericText] = await build_and_solve_mna(2, ["Y0"], components, values, pyodide, "sympy");
137171
expect(textResult).toEqual("G0*R0/(G0*R0 + 1)");
138172
// expect(_mathml).toEqual(null);
139-
expect(complex_response).toEqual("G0*R0/(G0*R0 + 1)");
173+
expect(complex_response).toEqual("0.999000999000999");
140174
expect(numericText).toEqual("0.999000999000999");
141175
});
142176

@@ -176,6 +210,6 @@ test("voltage in voltage probe VCVS", async () => {
176210
const [textResult, _mathml, complex_response, _numericResult, numericText] = await build_and_solve_mna(3, ["X0"], components, values, pyodide, "sympy");
177211
expect(textResult).toEqual("A0*R0*(C0*L0*s**2 + 1)/(C0*L0*R0*s**2 + L0*s + R0)");
178212
// expect(_mathml).toEqual(null);
179-
expect(complex_response).toEqual("Abs(A0*C0*L0*R0*s**2 - A0*R0)/sqrt(C0**2*L0**2*R0**2*s**4 - 2*C0*L0*R0**2*s**2 + L0**2*s**2 + R0**2)"); //FIXME - this seems to be a sympy bug
213+
expect(complex_response).toEqual("Abs(1.0e-13*s**2 - 100000000)/(1000000*sqrt(1.0e-42*s**4 - 1.999999999e-21*s**2 + 1))"); //FIXME - this seems to be a sympy bug
180214
expect(numericText).toEqual("(1.0e-13*s^2 + 100000000)/(1.0e-15*s^2 + 1.0e-9*s + 1000000)");
181215
});

toDo.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
#visiojs todo
1+
# Todo Oct 23rd
2+
- Update all the packages - once Sympy 1.13.4 is integrated to Pyodide!
3+
4+
# visiojs todo
25

36
- delete wire, redraw wire, then undo breaks
47
- Move the matrix solver into a web worker. So if can be killed with a timeout
@@ -26,6 +29,13 @@
2629

2730
- if add op amp with zero on input and LC on output, and change input to current source, it crashes
2831

32+
# Done October 2025
33+
34+
- Fix Algebrite which got broken when Sympy moved to async function
35+
- Fix self testing which got broken when numeric solving moved from js to sympy
36+
- Add an algebrite case to self testing
37+
- If Sympy crashes, don't hang for ever, feed it back to the user. It does this with a Toast
38+
2939
# Done on Jan 28th
3040

3141
- handle errors in calculate mna better

0 commit comments

Comments
 (0)