Phyth v0.1.0
A dimensional safe C++ header-only mechanics and electromagnetics simulation library.
Loading...
Searching...
No Matches
PointCharge.hpp
Go to the documentation of this file.
1#ifndef PHYTH_POINT_CHARGE_HPP
2#define PHYTH_POINT_CHARGE_HPP
3
4#include "ChargeSource.hpp"
5#include "MagneticSource.hpp"
8
44 public ChargeSource,
45 public MagneticSource {
46 public:
63
72 void SetComputeForcesFunction(const std::function<void(PointCharge *)> &func) noexcept {
73 Particle::SetComputeForcesFunction([func](Particle *p) {
74 func(dynamic_cast<PointCharge *>(p));
75 });
76 }
77
96 const auto r = point - position_;
97 if (r.Length() < Quantity<Meter>(Config::epsilon)) {
98 throw std::runtime_error("PointCharge: field diverges at charge location");
99 }
100
101 if (fixed_) {
102 return Consts::k_E * charge_value_ / r.LengthSquared() * r.Normalized();
103 }
105 try {
107 } catch (std::runtime_error &) {
109 if (!position_history_.IsEmpty()) {
110 retarded_r = point - position_history_.GetValueByTime(0_s);
111 // The observation point has not yet seen the movement of charges.
112 // It is an electrostatic field of charges at p_0.
113 }
114 return Consts::k_E * charge_value_ / retarded_r.LengthSquared() * retarded_r.Normalized();
115 }
116
120
121 const auto R = point - retarded_pos;
122 const auto hat_R = R.Normalized();
123 const auto beta = retarded_v / Consts::c;
124 const auto beta_a = retarded_a / Consts::c;
125 const auto gamma = 1 / Utils::sqrt(1_ - Utils::square(beta.Length()));
126
128 / Utils::square(gamma * R.Length())
129 / Utils::cube(1_ - hat_R.Dot(beta));
130
132 * hat_R.Cross((hat_R - beta).Cross(beta_a))
133 / Consts::c
134 / Utils::cube(1_ - hat_R.Dot(beta))
135 / R.Length();
137 }
138
152 if (fixed_) {
153 return {};
154 }
155 const auto r = point - position_;
157 try {
159 } catch (std::runtime_error &) {
160 return Consts::mu_0 / Consts::varpi * charge_value_ * velocity_.Cross(r.Normalized()) / r.
161 LengthSquared();
162 }
163 const auto R = point - GetPositionAtOffset(retarded_dt);
164 const auto hat_R = R.Normalized();
165
166 return (hat_R / Consts::c).Cross(GetElectricFieldAt(point));
167 }
168
183 const auto r = point - position_;
184 const auto dist = r.Length();
185
187 throw std::runtime_error("PointCharge: potential diverges at charge location");
188 }
189
190 if (fixed_ || position_history_.IsEmpty()) {
191 return Consts::k_E * charge_value_ / dist;
192 }
193
195 try {
197 } catch (std::runtime_error &) {
198 return Consts::k_E * charge_value_ / dist;
199 }
200
203
204 const auto R_vec = point - retarded_pos;
205 const auto R = R_vec.Length();
206 const auto hat_R = R_vec.Normalized();
207 const auto beta = retarded_v / Consts::c;
208
209 const auto denominator = R - hat_R.Dot(beta) * R;
210
212 throw std::runtime_error("PointCharge: line-of-sight aligns with charge velocity at light speed");
213 }
214
216 }
217
226
235
247
254 return charge_value_;
255 }
256
265 return (point - position_).Length();
266 }
267
274 void Integrate() noexcept override {
275 Particle::Integrate();
276
280 }
281
282 private:
284
288
292 if (offset == 0_s) {
293 return position_;
294 }
295 return position_history_.GetValueByOffset(offset);
296 }
297
301 if (offset == 0_s) {
302 return velocity_;
303 }
304 return velocity_history_.GetValueByOffset(offset);
305 }
306
310 if (offset == 0_s) {
311 return external_force_ / mass_;
312 }
313 return acceleration_history_.GetValueByOffset(offset);
314 }
315
324 if (position_history_.IsEmpty()) {
325 return 0_s;
326 }
327
330
331 const Quantity<Second> max_dt = static_cast<double>(position_history_.GetSize())
332 * Config::dt;
333 dt_delay = std::clamp(dt_delay, Quantity<Second>(0.0), max_dt);
334
335 for (int iter = 0; iter < Config::max_iterations; ++iter) {
338
340 const Quantity<Meter> R = R_vec.Length();
342
344
346 if (Utils::isnan(dt_delay)) {
347 throw std::runtime_error(
348 "PointCharge: signal has not reached observation point or no solution in interval");
349 }
350 return dt_delay;
351 }
352
354
356 break;
357 }
358
359 const Quantity<Second> delta = f / df;
361 dt_delay = std::clamp(dt_delay, Quantity<Second>(0.0), max_dt);
362 }
363
365 }
366
378 auto ComputeResidual = [&](const Quantity<Second> dt) -> Quantity<Meter> {
380 const Quantity<Meter> R = (point - pos_r).Length();
381 return R - Consts::c * dt;
382 };
383
386
387 if (f_low * f_high > 0_m2) {
388 throw std::runtime_error(
389 "PointCharge: signal has not reached observation point or no solution in interval");
390 }
391
392 for (int iter = 0; iter < Config::max_iterations; ++iter) {
393 const Quantity<Second> dt_mid = (dt_low + dt_high) / 2;
395
397 return dt_mid;
398 }
399
400 if (f_low * f_mid < 0_m2) {
401 dt_high = dt_mid;
402 f_high = f_mid;
403 } else {
404 dt_low = dt_mid;
405 f_low = f_mid;
406 }
407 }
408
409 throw std::runtime_error("PointCharge: bisection failed to converge");
410 }
411 };
412}
413
414#endif // PHYTH_POINT_CHARGE_HPP
Abstract base class for all charge distributions.
Abstract base class for all magnetic field sources.
Point charge with full electromagnetic field and dynamics.
void ApplyElectricFieldForce(const Vector3< Quantity< NewtonPerCoulomb > > &field)
Apply force from an electric field: F = q * E.
TimeHistory< Vector3< Quantity< MeterPerSecond > > > velocity_history_
Vector3< Quantity< Meter > > GetPositionAtOffset(const Quantity< Second > &offset) const
Get position at a time offset in the past.
PointCharge(const Quantity< Kilogram > mass, const Vector3< Quantity< Meter > > &position, const Quantity< Coulomb > charge_value)
Construct a point charge.
Quantity< Volt > GetElectricPotentialAt(const Vector3< Quantity< Meter > > &point) const override
Get the electric potential at a point.
TimeHistory< Vector3< Quantity< Meter > > > position_history_
Vector3< Quantity< MeterPerSecondSquared > > GetAccelerationAtOffset(const Quantity< Second > &offset) const
Get acceleration at a time offset in the past.
Quantity< Meter > GetMinimumDistanceTo(const Vector3< Quantity< Meter > > &point) const override
Get the minimum distance from a point to the charge location.
Quantity< Second > SolveRetardedTimeOffset(const Vector3< Quantity< Meter > > &point) const
Solve for the retarded time offset using Newton's method.
Vector3< Quantity< Tesla > > GetMagneticFieldAt(const Vector3< Quantity< Meter > > &point) const override
Get the magnetic field at a point using Liénard-Wiechert formulas.
Vector3< Quantity< NewtonPerCoulomb > > GetElectricFieldAt(const Vector3< Quantity< Meter > > &point) const override
Get the electric field at a point using Liénard-Wiechert formulas.
Vector3< Quantity< MeterPerSecond > > GetVelocityAtOffset(const Quantity< Second > &offset) const
Get velocity at a time offset in the past.
TimeHistory< Vector3< Quantity< MeterPerSecondSquared > > > acceleration_history_
Quantity< Coulomb > GetTotalChargeValue() const override
Get the total charge value.
void ApplyLorentzForce(const Vector3< Quantity< NewtonPerCoulomb > > &electric_field, const Vector3< Quantity< Tesla > > &magnetic_field)
Apply the full Lorentz force: F = q * (E + v cross B)
Quantity< Second > SolveRetardedTimeOffsetBisection(const Vector3< Quantity< Meter > > &point, Quantity< Second > dt_low, Quantity< Second > dt_high) const
Solve for the retarded time offset using bisection method.
void SetComputeForcesFunction(const std::function< void(PointCharge *)> &func) noexcept
Set the force computation function.
void ApplyMagneticFieldForce(const Vector3< Quantity< Tesla > > &field)
Apply force from a magnetic field: F = q * v cross B.
void Integrate() noexcept override
Integrate dynamics using velocity Verlet.
Physical particle with mass, position, velocity, and force accumulation.
Definition Particle.hpp:36
Quantity< Kilogram > mass_
Definition Particle.hpp:197
Vector3< Quantity< MeterPerSecond > > velocity_
Definition Particle.hpp:198
Vector3< Quantity< Meter > > position_
Definition Particle.hpp:202
Vector3< Quantity< Newton > > external_force_
Definition Particle.hpp:201
Particle(const Quantity< Kilogram > &mass, const Vector3< Quantity< Meter > > &position) noexcept
Construct a particle with mass and initial position.
Definition Particle.hpp:46
void ApplyForce(const Vector3< Quantity< Newton > > &force)
Accumulate an external force on the particle.
Definition Particle.hpp:63
Time-based history with linear interpolation.
int max_iterations
Definition Config.hpp:14
double epsilon
Definition Config.hpp:13
double tolerance
Definition Config.hpp:15
constexpr auto k_E
constexpr auto c
constexpr auto mu_0
constexpr double varpi
Definition Constants.hpp:8
bool isnan(Quantity< UnitT > x)
Determine whether a quantity is nan.
auto square(Quantity< UnitT > q)
Square of a quantity.
auto sqrt(Quantity< UnitT > q)
Square root of a quantity.
auto cube(Quantity< UnitT > q)
Cube of a quantity.
auto abs(Quantity< UnitT > q)
Absolute value of a quantity.
Dimension< std::ratio< 1 >, std::ratio< 0 >, std::ratio< 0 >, std::ratio< 0 >, std::ratio< 0 >, std::ratio< 0 >, std::ratio< 0 > > Length
constexpr bool is_unit_v
Definition Unit.hpp:79
Physical quantity with a value and a compile-time unit.
Definition Quantity.hpp:27
3D vector with Quantity components
Definition Vector3.hpp:28
Vector3< Scalar > Normalized() const
Normalized vector (unit vector in the same direction)
Definition Vector3.hpp:382