Special relativity · Electrodynamics §9

Charge density is frame-dependent, while total charge is invariant.

How do charge density and current density transform between inertial frames, and why is a neutral current-carrying wire charged in a moving frame?

Special relativity §9

Charge and current density in moving frames

Static worked example

Model note
  • Charge density (stationary system) (chargeDensityStationary), Charge density (moving system) (chargeDensityMoving), Current density (stationary system) (currentDensityStationary) and Current density (moving system) (currentDensityMoving): Host calculation, by the site's own reference code. Computed in fields.transformChargeCurrent.
  • Four-current invariant (cρ)² − |J|² (fourCurrentInvariant) and Four-current invariant (cρ)² − |J|², normalized (fourCurrentInvariantNormalized): Host calculation, by the site's own reference code. Computed in fields.fourCurrentInvariants.
  • Lorentz factor (lorentzFactor): Host calculation, by the site's own reference code. Computed in kinematics.gamma.
  • Continuity residual ∂ρ/∂t + div J in K (continuityResidualStationary) and Continuity residual ∂ρ/∂t + div J in k (continuityResidualMoving): Host calculation, by the site's own reference code. Computed in fields.gaussianPulseContinuity.
  • Charge on the loop's top leg (+x), in k (loopLegChargePositive), Charge on the loop's bottom leg (−x), in k (loopLegChargeNegative) and Total charge on the loop, in k (loopTotalCharge): Host calculation, by the site's own reference code. Computed in fields.currentLoopCharges.
  • Total charge on the sphere, Q, in K (sphereTotalChargeStationary) and Total charge on the sphere, Q, in k (sphereTotalChargeMoving): Host calculation, by the site's own reference code. Computed in fields.sphereTotalCharge.
  • Computed from the settings last applied (settings revision 1, result 1).
  • Not modeled: microscopic lattice dynamics and thermal vibrations; self-inductance and transient current startup; radiation reaction from accelerated charges; material resistance and Joule heating; finite wire thickness effects; gravitational fields and general relativistic curvature.

Predict before the numbers

A neutral wire in the laboratory carries a current in the +x direction. Described from a frame moving in the +x direction at 0.6c, is the wire still electrically neutral?

Three relations the model could have

The result appears when you choose, say you have one in mind, or skip.

A neutral wire carrying a current

v = 0.60c, γ = 1.25

Laboratory frame K

ρ = 0 C/m³, Jx = 1 A/m²

electrons drift

Ions and electrons are equally spaced, so the wire is neutral. The electrons drift left, which is a current to the right.

Frame k, moving at v = 0.60c

ρ′ = −2.5017 × 10⁻⁹ C/m³, J′x = 1.25 A/m²

wire moves

Seen from k, the whole wire moves left and both rows close up by γ = 1.25. The electrons close up very slightly more than the ions, so the wire carries a net negative charge. The drawing exaggerates the difference to one electron; at a real drift speed it is far smaller.

Experiment settings the charge density and the current density along x
Set 0 for neutral conductor
Conduction current along x

Changes here apply with Apply parameters.

Worked example: in the laboratory the charge density is 0 C/m³ (neutral); described from the frame moving at 0.6c it is −2.5 × 10⁻⁹ C/m³ (negatively charged).

QuantityStationary frame (K)Moving frame (k)UnitLorentz transformation law
Charge density ρ0−2.5017 × 10⁻⁹C/m³ρ′ = γ(ρ − vJx/c²)
Current density Jx(1, 0, 0)(1.25, 0, 0)A/m²J′x = γ(Jx − vρ)
Lorentz factor γ1.2511 / √(1 − v²/c²)
Four-current invariant (cρ)² − |J|²−1A²/m⁴Exact scalar invariant across all frames

A wire carrying a current, with no net charge for an observer standing beside it, has a net charge for an observer moving along it. Charge and current mix under a change of frame, while the total charge of a body stays the same.

§9 takes the Maxwell–Hertz equations with convection currents, in which ρ is 4π times the density of electricity and (ux, uy, uz) its velocity. For charges bound to small rigid bodies, ions and electrons, these equations are the foundation of Lorentz's electrodynamics of moving bodies. Transforming them by §3 and §6, Einstein finds the same equations in k, provided the velocity of the charges transforms by the addition theorem of §5 and the density by ρ′ = β(1 − v ux/V²)ρ. So Lorentz's foundation agrees with the principle of relativity, and a charged body whose charge does not change in its own frame keeps a constant charge seen from K as well. The lab's default is a neutral conductor carrying 1 A/m² along x, seen from a frame moving at 0.6c along the current. In that frame the current density is 1.25 A/m², and the conductor carries a net charge density of −2.50 × 10−9 C/m³ where the stationary observer found none. For a rectangular loop carrying 1 A, 1 m long, the two legs parallel to the motion pick up ±2.00 × 10−9 C, and the loop's total stays zero. The lab also checks two things the paper does not state in this form: (cρ)² − |J|² is −1 A²/m⁴ in both frames, and charge conservation, ∂ρ/∂t + div J = 0, holds in both.

Modern qualifications

§9 writes ρ for 4π times the density of electricity and (ux, uy, uz) for its velocity, in the units of §6. It proves only that the transformed equations keep their form, with charge density and velocity transformed as stated, and draws from them the constancy of a body's charge; it does not write a four-current or an invariant, and it does not discuss the continuity equation. Joining charge and current density into one four-vector, with (cρ)² − |J|² unchanged, is the later language of Poincaré and Minkowski. That a wire carrying a current appears charged to a moving observer is a standard modern consequence, often used to explain magnetism from electrostatics and relativity, and the paper does not discuss it.

Not modeled: microscopic lattice dynamics and thermal vibrations, self-inductance and transient current startup, radiation reaction from accelerated charges, material resistance and Joule heating, finite wire thickness effects, gravitational fields and general relativistic curvature.
Show the code

The site’s own reference code for this law, in TypeScript. It computes the numbers above when the page runs it, and it is what the page uses wherever a FrankenSim result is not available.

evaluateSr12 · src/physics/reference/fields.ts · this site’s source, as built · sha256:2456d6085eed656243ff791b623bb875b6b857f37d0460ec80a5545dc81b1e9a

This function computes the listed outputs when it runs.

In words

It transforms the four-current, computes its invariant, evaluates the continuity residual in each frame, and totals the charge on a sphere and around a current loop. The heart of it is that charge is invariant while charge DENSITY is not: a sphere carries the same total charge in both descriptions, and the function returns the two totals separately so a reader can see them agree rather than being told they do.

Implementation

export function evaluateSr12(input: Sr12Input): Sr12Snapshot {
  const c = C_SI;
  const v = input.boost;
  const eventId = "event-charge-current-0";

  const packScalar = (
    key: keyof typeof SR12_ROWS,
    make: (id: string, unit: string, kind: string, owner: string) => ScientificResult,
  ) => {
    const r = SR12_ROWS[key];
    return make(r[0], r[1], r[2], r[3]);
  };

  const valScalar = (key: keyof typeof SR12_ROWS, num: number) =>
    packScalar(key, (id, unit, kind, owner) => asValue(id, unit, kind, owner, num));

  const valVector = (key: keyof typeof SR12_ROWS, vec: Vec3) =>
    packScalar(key, (id, unit, kind, owner) => asVectorValue(id, unit, kind, owner, vec));

  const refuseAll = (reason: string, condition: string): Sr12Snapshot => {
    const out = (key: keyof typeof SR12_ROWS) =>
      packScalar(key, (id, unit, kind, owner) =>
        asOutside(id, unit, kind, owner, condition, reason),
      );
    return Object.freeze({
      beta: v / c,
      gamma: Number.NaN,
      chargeDensityStationary: out("rhoM"),
      chargeDensityMoving: out("rhoC"),
      currentDensityStationary: out("JM"),
      currentDensityMoving: out("JC"),
      fourCurrentInvariant: out("inv"),
      fourCurrentInvariantNormalized: out("invNorm"),
      lorentzFactor: out("gamma"),
      continuityResidualStationary: out("contM"),
      continuityResidualMoving: out("contC"),
      loopLegChargePositive: out("legPos"),
      loopLegChargeNegative: out("legNeg"),
      loopTotalCharge: out("loopTotal"),
      sphereTotalChargeStationary: out("sphereM"),
      sphereTotalChargeMoving: out("sphereC"),
      eventId,
    });
  };

  if (
    ![
      input.boost,
      input.chargeDensity,
      input.currentDensity.x,
      input.currentDensity.y,
      input.currentDensity.z,
      input.carrierVelocity.x,
      input.carrierVelocity.y,
      input.carrierVelocity.z,
      input.sphereRadius,
      input.sphereCharge,
      input.loopCurrent,
      input.loopLengthX,
      input.loopLengthY,
      input.pulseWidth,
      input.pulseAmplitude,
    ].every(Number.isFinite)
  ) {
    return refuseAll(
      "All density, current, velocity and boost inputs must be finite numbers.",
      "finite inputs",
    );
  }

  const beta = v / c;
  if (Math.abs(beta) > 0.95) {
    return refuseAll("Observer boost speed must satisfy |v| <= 0.95c.", "|v| <= 0.95c");
  }

  const g = gamma(beta);
  if (g.status !== "value") {
    return refuseAll("Lorentz factor could not be evaluated.", "|v| < c");
  }
  const γ = g.value;

  const rho0 = input.chargeDensity;
  let J0: Vec3 = input.currentDensity;

  if (input.mode === "convection") {
    const uMagSq =
      input.carrierVelocity.x ** 2 + input.carrierVelocity.y ** 2 + input.carrierVelocity.z ** 2;
    if (uMagSq >= c * c) {
      return refuseAll("Carrier velocity must satisfy |u| < c.", "|u| < c");
    }
    J0 = Object.freeze({
      x: rho0 * input.carrierVelocity.x,
      y: rho0 * input.carrierVelocity.y,
      z: rho0 * input.carrierVelocity.z,
    });
  }

  const transformed = transformChargeCurrent({ rho: rho0, J: J0, boost: v, c });
  const invs = fourCurrentInvariants(rho0, J0, c);

  // Mode-specific calculations
  const loop = currentLoopCharges(input.loopCurrent, input.loopLengthX, input.loopLengthY, v, c);

  const sphereQ0 = sphereTotalCharge(input.sphereRadius, rho0);
  const sphereQPrime = sphereQ0; // Exact charge invariance

  const pulse = gaussianPulseContinuity(
    input.pulseAmplitude,
    input.carrierVelocity.x,
    input.pulseWidth,
    0,
    0,
    v,
    c,
  );

  return Object.freeze({
    beta,
    gamma: γ,
    chargeDensityStationary: valScalar("rhoM", rho0),
    chargeDensityMoving: valScalar("rhoC", transformed.rho),
    currentDensityStationary: valVector("JM", J0),
    currentDensityMoving: valVector("JC", transformed.J),
    fourCurrentInvariant: valScalar("inv", invs.si),
    fourCurrentInvariantNormalized: valScalar(
      "invNorm",
      (c * rho0) ** 2 / (c * c) - (J0.x ** 2 + J0.y ** 2 + J0.z ** 2) / (c * c),
    ),
    lorentzFactor: valScalar("gamma", γ),
    continuityResidualStationary: valScalar("contM", pulse.stationaryResidual || 0),
    continuityResidualMoving: valScalar("contC", pulse.movingResidual || 0),
    loopLegChargePositive: valScalar("legPos", loop.legChargePositive),
    loopLegChargeNegative: valScalar("legNeg", loop.legChargeNegative),
    loopTotalCharge: valScalar("loopTotal", loop.totalCharge),
    sphereTotalChargeStationary: valScalar("sphereM", sphereQ0),
    sphereTotalChargeMoving: valScalar("sphereC", sphereQPrime),
    eventId,
  });
}

The site’s own reference code for this law, in TypeScript. It computes the numbers above when the page runs it, and it is what the page uses wherever a FrankenSim result is not available.

transformChargeCurrent · src/physics/reference/fields.ts · this site’s source, as built · sha256:4efbed0926134abf4e2db73308635313f2cfefb0971f46939053c1346f13dfad

This function computes the listed outputs when it runs.

In words

The transformed density is gamma times the density less the current along the boost over c squared; the transformed current along the boost is gamma times the current less the density times the velocity. The components across the boost pass through. Written side by side with the coordinate transformation the pattern is the same, which is the point of showing this function next to that one: a density and a current form a four-vector in the same way a time and a position do.

Implementation

export function transformChargeCurrent(input: {
  rho: number;
  J: Vec3;
  boost: number;
  c?: number;
}): Readonly<{
  rho: number;
  J: Vec3;
  beta: number;
  gamma: number;
}> {
  const c = input.c ?? C_SI;
  const v = input.boost;
  const beta = v / c;
  const g = gamma(beta);
  if (g.status !== "value") {
    return Object.freeze({
      rho: Number.NaN,
      J: Object.freeze({ x: Number.NaN, y: Number.NaN, z: Number.NaN }),
      beta,
      gamma: Number.NaN,
    });
  }
  const γ = g.value;
  const { rho, J } = input;
  const rhoPrime = γ * (rho - (v * J.x) / (c * c));
  const JprimeX = γ * (J.x - v * rho);
  return Object.freeze({
    rho: rhoPrime,
    J: Object.freeze({
      x: JprimeX,
      y: J.y,
      z: J.z,
    }),
    beta,
    gamma: γ,
  });
}

Worked case (readable without JavaScript)

Consider a neutral conductor in the stationary frame K with volumetric charge density ρ = 0 and current density Jx = 1 A/m², viewed from a frame k boosted along x at speed v = 0.6c (γ = 1.25).

ρ′=γ(ρ−vJxc2)=1.25(0−0.6c)=−0.75c≈−2.5017×10−9 C/m3\begin{aligned}\rho' &= \gamma\left(\rho - \frac{v J_x}{c^2}\right) \\ &= 1.25\left(0 - \frac{0.6}{c}\right) \\ &= -\frac{0.75}{c} \\ &\approx -2.5017\times 10^{-9}\text{ C/m}^3\end{aligned}
Jx′=γ(Jx−vρ)=1.25(1−0)=1.25 A/m2\begin{aligned}J'_x &= \gamma\left(J_x - v\rho\right) \\ &= 1.25(1 - 0) \\ &= 1.25\text{ A/m}^2\end{aligned}

Both coordinate frames agree exactly on the relativistic four-current invariant (cρ)² − |J|²:

(cρ)2−Jx2=0−1=−1 (A/m2)2,(cρ′)2−(Jx′)2=(−0.75)2−(1.25)2=0.5625−1.5625=−1 (A/m2)2\begin{gathered}(c\rho)^2 - J_x^2 = 0 - 1 = -1\text{ (A/m}^2)^2, \\ \begin{aligned}(c\rho')^2 - (J'_x)^2 &= (-0.75)^2 - (1.25)^2 \\ &= 0.5625 - 1.5625 \\ &= -1\text{ (A/m}^2)^2\end{aligned}\end{gathered}

For a rectangular current loop of length lx = 1 m carrying current I = 1 A at 0.6c, Lorentz contraction shortens the x-legs to l′x = lx/γ = 0.8 m. The top leg carries charge q′+ = −(v I lx)/c² ≈ −2.0014×10⁻⁹ C while the bottom leg carries q′− = +(v I lx)/c² ≈ +2.0014×10⁻⁹ C. The total charge remains identically zero, verifying that total charge is an exact Lorentz scalar.

Embed this laboratory on another page. It starts from its worked defaults, not your current settings.