Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/(core)/data/articles/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export const allBlogs = {
[projectileParabolicBlog.slug]: projectileParabolicBlog,
[threeBodyProblemBlog.slug]: threeBodyProblemBlog,
[piCollisionBlog.slug]: piCollisionBlog,
[rayTracingBlog.slug]: rayTracingBlog,
[kirchhoffLawsBlog.slug]: kirchhoffLawsBlog,
};

export const blogsArray = Object.values(allBlogs);
437 changes: 437 additions & 0 deletions app/(core)/data/articles/kirchhoffs-circuit-laws-explained.js

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions app/(core)/data/chapters.js
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,18 @@ const chapters = [
thumbnail: "/thumbnails/ray-tracing.webp",
relatedBlogSlug: "how-does-ray-tracing-work",
},
{
id: 17,
name: "Kirchhoff's Circuit Laws",
desc: "Build and solve DC circuits online: single loop, series, parallel and a two-loop network with two cells. Watch charge flow, drag a voltmeter and an ammeter onto the circuit, and see Kirchhoff's current and voltage laws balance to zero in real time.",
link: "/simulations/KirchhoffCircuit",
level: "upperSecondary",
alsoFor: ["lowerSecondary"],
difficulty: "extended",
tags: [TAGS.ELECTROMAGNETISM, TAGS.ENERGY, TAGS.INTERACTIVE],
thumbnail: "/thumbnails/kirchhoff-circuit.webp",
relatedBlogSlug: "kirchhoffs-circuit-laws-explained",
},
];

export default chapters;
206 changes: 206 additions & 0 deletions app/(core)/data/configs/KirchhoffCircuit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
// app/(core)/data/configs/KirchhoffCircuit.js
//
// UI schema and readout for the Kirchhoff circuit simulation. The circuit
// topology, the nodal solver and the drawing all live in
// simulations/KirchhoffCircuit.jsx β€” nothing physical belongs here.

/**
* The circuits the learner can switch between. The value is the preset id the
* simulation builds; changing it changes *what exists*, so the simulation
* rebuilds the world rather than merely re-reading a parameter.
*/
export const CIRCUIT_PRESETS = [
{ value: "single", label: "Single loop β€” E, r, R₁" },
{ value: "series", label: "Series β€” R₁ + Rβ‚‚ + R₃" },
{ value: "parallel", label: "Parallel β€” R₁ βˆ₯ Rβ‚‚ βˆ₯ R₃" },
{ value: "twoLoop", label: "Two-loop network β€” E₁ and Eβ‚‚" },
];

/**
* Defaults are chosen so the two-loop network opens on its most instructive
* state: Eβ‚‚ ends up with a *negative* branch current, i.e. the second cell is
* being charged by the first. See the article for the worked numbers.
*/
export const INITIAL_INPUTS = {
preset: "twoLoop",

emf1: 12,
internalR1: 0.5,
emf2: 6,
internalR2: 0.5,

r1: 4,
r2: 6,
r3: 3,

showChargeFlow: true,
showCurrentArrows: true,
showGlow: true,
showProbes: true,

wireColor: "#94a3b8",
};

export const INPUT_FIELDS = [
{
name: "preset",
label: "Circuit",
type: "select",
options: CIRCUIT_PRESETS,
},

{
name: "emf1",
label: "First cell EMF",
symbol: "E₁",
unit: "V",
type: "number",
min: 0,
max: 24,
step: 0.5,
},
{
name: "internalR1",
label: "First cell internal resistance",
symbol: "r₁",
unit: "Ξ©",
type: "number",
min: 0.1,
max: 5,
step: 0.1,
},
{
name: "emf2",
label: "Second cell EMF",
symbol: "Eβ‚‚",
unit: "V",
type: "number",
min: 0,
max: 24,
step: 0.5,
},
{
name: "internalR2",
label: "Second cell internal resistance",
symbol: "rβ‚‚",
unit: "Ξ©",
type: "number",
min: 0.1,
max: 5,
step: 0.1,
},

{
name: "r1",
label: "First resistor",
symbol: "R₁",
unit: "Ξ©",
type: "number",
min: 0.5,
max: 40,
step: 0.5,
},
{
name: "r2",
label: "Second resistor",
symbol: "Rβ‚‚",
unit: "Ξ©",
type: "number",
min: 0.5,
max: 40,
step: 0.5,
},
{
name: "r3",
label: "Third resistor",
symbol: "R₃",
unit: "Ξ©",
type: "number",
min: 0.5,
max: 40,
step: 0.5,
},

{ name: "showChargeFlow", label: "Animate charge flow", type: "checkbox" },
{ name: "showCurrentArrows", label: "Show current arrows", type: "checkbox" },
{ name: "showGlow", label: "Glow with power dissipated", type: "checkbox" },
{ name: "showProbes", label: "Show meter probes", type: "checkbox" },
{ name: "wireColor", label: "Wire color", type: "color" },
];

const volts = (v) => `${v.toFixed(2)} V`;
const amps = (i) => `${i.toFixed(3)} A`;
const watts = (w) => `${w.toFixed(2)} W`;

/** A signed term formatted for a running sum: "+12.00", "βˆ’5.51". */
const term = (v) =>
`${Math.abs(v) >= 5e-3 && v < 0 ? "βˆ’" : "+"}${Math.abs(v).toFixed(2)}`;

/**
* Residuals are floating-point dust, not physics β€” treat anything tiny as 0.
* `snap` also keeps a βˆ’1e-16 sum from printing as "βˆ’0.00 V", which in a readout
* whose whole point is "this lands on zero" reads like a failure.
*/
const TOLERANCE = 1e-6;
const snap = (v) => (Math.abs(v) < TOLERANCE ? 0 : v);
const verdict = (residual) => (Math.abs(residual) < TOLERANCE ? "βœ“" : "βœ—");

export const SimInfoMapper = (state) => {
if (!state || !state.ok) {
return {
Circuit: state?.presetLabel ?? "β€”",
Status: "No solution β€” check that every resistance is above zero",
};
}

const rows = { Circuit: state.presetLabel };

for (const node of state.nodes) {
rows[`V (${node.label})`] = node.isGround
? "0.00 V (reference)"
: volts(node.V);
}

for (const branch of state.branches) {
const direction =
branch.I < 0 ? ` (flows ${branch.to}β†’${branch.from})` : "";
rows[`I (${branch.label})`] = `${amps(branch.I)}${direction}`;
}

// Kirchhoff's current law: what goes into a junction comes back out.
for (const node of state.kcl) {
rows[`KCL at ${node.label}`] =
`in ${node.inSum.toFixed(3)} = out ${node.outSum.toFixed(3)} A ${verdict(
node.residual
)}`;
}

// Kirchhoff's voltage law: walk the loop, add every rise and drop, land on 0.
for (const loop of state.loops) {
rows[`KVL loop ${loop.label}`] =
`${loop.terms.map(term).join(" ")} = ${snap(loop.sum).toFixed(
2
)} V ${verdict(loop.sum)}`;
}

for (const part of state.dissipation) {
rows[`P (${part.label})`] = watts(part.P);
}

// Kept the same length as the row above so the two totals, which should always
// agree, line up in the panel instead of one of them wrapping.
rows["P delivered by cells"] = watts(state.powerDelivered);
rows["P dissipated as heat"] = watts(state.powerDissipated);

if (state.voltmeter) {
rows["Voltmeter"] =
`${state.voltmeter.from} β†’ ${state.voltmeter.to} = ${volts(
state.voltmeter.reading
)}`;
}
if (state.ammeter) {
rows["Ammeter"] = `${state.ammeter.label} = ${amps(state.ammeter.reading)}`;
}

return rows;
};
45 changes: 45 additions & 0 deletions app/(core)/data/simulationOverviews.js
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,51 @@ const simulationOverviews = {
"law-of-reflection",
],
},

KirchhoffCircuit: {
intro:
"Four DC circuits β€” a single loop, three resistors in series, three in parallel, and a two-loop network driven by two cells β€” solved live by Kirchhoff's laws. Charge carriers drift along each wire at a speed set by that branch's current, resistors glow with the power they dissipate, and the readout walks each loop term by term so you can watch the voltages add up to exactly zero.",
controls: [
"Circuit layout",
"EMF of each cell",
"Internal resistance of each cell",
"The three resistances",
],
concepts: [
"Kirchhoff's current law (junction rule) as charge conservation",
"Kirchhoff's voltage law (loop rule) as energy conservation",
"Ohm's law and equivalent resistance in series and parallel",
"EMF, internal resistance and terminal voltage",
"Power delivered by a cell and dissipated as heat",
"Sign conventions: a negative branch current means a cell is charging",
],
formulas: [
{
label: "Ohm's law",
latex: "V = IR",
},
{
label: "Junction rule (KCL)",
latex: "\\sum_{\\text{into node}} I = \\sum_{\\text{out of node}} I",
},
{
label: "Loop rule (KVL)",
latex: "\\sum_{\\text{closed loop}} \\Delta V = 0",
},
{
label: "Current in a branch of EMF E and resistance R",
latex: "I = \\dfrac{V_{\\text{from}} - V_{\\text{to}} + E}{R}",
},
{
label: "Terminal voltage of a real cell",
latex: "V_{\\text{term}} = E - I r",
},
{
label: "Power dissipated in a resistance",
latex: "P = I^{2} R",
},
],
},
};

export default simulationOverviews;
Binary file added public/thumbnails/kirchhoff-circuit.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading