86 for (
int k = 0; k < NZ; ++k)
87 for (
int j = 0; j < NY; ++j)
88 for (
int i = 0; i < NX; ++i)
89 sigma.
grid().
set(i, j, k, inside_rod(i,k) ? ROD_SIG : BG_SIG);
91 std::vector<physics::ElectrodeBC> bcs;
92 for (
int k = 0; k < NZ; ++k)
93 for (
int i = 0; i < NX; ++i) {
94 if (!inside_rod(i, k))
continue;
95 bcs.push_back({ flat(i, 0, k), V_BOT });
96 bcs.push_back({ flat(i, NY-1, k), V_TOP });
99 printf(
"Solving... ");
101 auto t0 = std::chrono::steady_clock::now();
109 double solve_ms = std::chrono::duration<double,std::milli>(
110 std::chrono::steady_clock::now() - t0).count();
111 printf(
"done in %.0f ms\n", solve_ms);
113 const int W = 1280, H = 800;
114 InitWindow(W, H,
"EM Demo -- rod + magnet");
117 Camera3D camera = {};
118 camera.up = { 0.0f, 1.0f, 0.0f };
120 camera.projection = CAMERA_PERSPECTIVE;
122 Vector3 orbit_target = { SIM_DOMAIN*0.5f, SIM_DOMAIN*0.5f, SIM_DOMAIN*0.5f };
123 float azimuth = 0.785f;
124 float elevation = 0.524f;
125 float distance = SIM_DOMAIN * 2.6f;
127 auto update_cam = [&]() {
129 orbit_target.x + distance * cosf(elevation) * sinf(azimuth),
130 orbit_target.y + distance * sinf(elevation),
131 orbit_target.z + distance * cosf(elevation) * cosf(azimuth)
133 camera.target = orbit_target;
136 float mag_x = ROD_CX * DX + 0.45f;
137 float mag_y = SIM_DOMAIN * 0.5f;
138 float mag_z = ROD_CZ * DX;
141 while (!WindowShouldClose()) {
142 bool shift = IsKeyDown(KEY_LEFT_SHIFT) || IsKeyDown(KEY_RIGHT_SHIFT);
145 if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) {
146 Vector2 d = GetMouseDelta();
149 Vector3 view = Vector3Normalize(
150 Vector3Subtract(camera.target, camera.position));
151 Vector3 right = Vector3Normalize(
152 Vector3CrossProduct(view, camera.up));
155 float spd = distance * 0.0012f;
156 mag_x += right.x * d.x * spd;
157 mag_z += right.z * d.x * spd;
160 azimuth -= d.x * 0.005f;
161 elevation += d.y * 0.005f;
162 if (elevation > 1.55f) elevation = 1.55f;
163 if (elevation < -1.55f) elevation = -1.55f;
168 float scroll = GetMouseWheelMove();
169 if (scroll != 0.0f) {
170 distance -= scroll * distance * 0.08f;
171 if (distance < 0.05f) distance = 0.05f;
175 if (IsMouseButtonDown(MOUSE_RIGHT_BUTTON)) {
176 Vector2 d = GetMouseDelta();
177 float spd = distance * 0.001f;
178 float raz = azimuth + 1.5708f;
179 orbit_target.x += (cosf(raz) * d.x
180 - sinf(elevation) * sinf(azimuth) * d.y) * spd;
181 orbit_target.y -= cosf(elevation) * d.y * spd;
182 orbit_target.z += (sinf(raz) * d.x
183 - sinf(elevation) * cosf(azimuth) * d.y) * spd;
189 float B_max = 1e-30f;
190 for (
int j = 0; j < NY; j += STRIDE)
191 for (
int k = 0; k < NZ; k += STRIDE)
192 for (
int i = 0; i < NX; i += STRIDE) {
193 float wx = i*DX, wy = j*DX, wz = k*DX;
194 float bx = (float)B.
x.
grid()(i, j, k);
195 float by = (float)B.
y.
grid()(i, j, k);
196 float bz = (float)B.
z.
grid()(i, j, k);
198 dipole_B(wx, wy, wz, mag_x, mag_y, mag_z, dbx, dby, dbz);
199 bx += dbx; by += dby; bz += dbz;
200 float mag = sqrtf(bx*bx + by*by + bz*bz);
201 if (mag > B_max) B_max = mag;
206 ClearBackground({ 15, 15, 20, 255 });
211 DrawCubeWires({ SIM_DOMAIN*0.5f, SIM_DOMAIN*0.5f, SIM_DOMAIN*0.5f },
212 SIM_DOMAIN, SIM_DOMAIN, SIM_DOMAIN, { 60, 60, 80, 255 });
215 float rod_wr = ROD_R * DX;
216 DrawCylinder({ ROD_CX*DX, 0.0f, ROD_CZ*DX },
217 rod_wr, rod_wr, SIM_DOMAIN, 24, { 180, 185, 195, 220 });
218 DrawCylinderWires({ ROD_CX*DX, 0.0f, ROD_CZ*DX },
219 rod_wr, rod_wr, SIM_DOMAIN, 24, { 100, 110, 130, 255 });
221 DrawCylinder({ ROD_CX*DX, SIM_DOMAIN - DX*0.5f, ROD_CZ*DX },
222 rod_wr, rod_wr, DX*0.5f, 24, { 220, 60, 60, 255 });
223 DrawCylinder({ ROD_CX*DX, 0.0f, ROD_CZ*DX },
224 rod_wr, rod_wr, DX*0.5f, 24, { 60, 80, 220, 255 });
227 DrawSphere({ mag_x, mag_y + MAG_VIS_R*0.5f, mag_z }, MAG_VIS_R, { 210, 60, 60, 255 });
228 DrawSphere({ mag_x, mag_y - MAG_VIS_R*0.5f, mag_z }, MAG_VIS_R, { 60, 80, 210, 255 });
230 DrawLine3D({ mag_x, mag_y - MAG_VIS_R*1.6f, mag_z },
231 { mag_x, mag_y + MAG_VIS_R*1.6f, mag_z }, WHITE);
236 static constexpr float B_THRESH = 0.002f;
237 for (
int j = 0; j < NY; j += STRIDE)
238 for (
int k = 0; k < NZ; k += STRIDE)
239 for (
int i = 0; i < NX; i += STRIDE) {
240 float wx = i*DX, wy = j*DX, wz = k*DX;
241 float bx = (float)B.
x.
grid()(i, j, k);
242 float by = (float)B.
y.
grid()(i, j, k);
243 float bz = (float)B.
z.
grid()(i, j, k);
245 dipole_B(wx, wy, wz, mag_x, mag_y, mag_z, dbx, dby, dbz);
246 bx += dbx; by += dby; bz += dbz;
248 float mag = sqrtf(bx*bx + by*by + bz*bz);
249 float t = mag / B_max;
250 if (t < B_THRESH)
continue;
253 float t_log = logf(1.0f + (t / B_THRESH - 1.0f)) /
254 logf(1.0f + (1.0f / B_THRESH - 1.0f));
255 float len = MAX_LEN * (0.15f + 0.85f * t_log);
256 draw_arrow({ wx, wy, wz }, { bx/mag, by/mag, bz/mag }, len,
263 DrawRectangle(8, 8, 440, 148, { 0, 0, 0, 160 });
264 DrawText(
"Aluminum Rod + Permanent Magnet", 16, 14, 18, WHITE);
266 snprintf(buf,
sizeof(buf),
"Solve: %.0f ms | phi iters=%llu res=%.1e",
267 solve_ms, (
unsigned long long)r_phi.iterations, r_phi.residual);
268 DrawText(buf, 16, 38, 14, LIGHTGRAY);
269 snprintf(buf,
sizeof(buf),
"Magnet (%.2f, %.2f, %.2f) M0=%.3f A*m^2",
270 (
double)mag_x, (
double)mag_y, (
double)mag_z, (
double)MAG_M0);
271 DrawText(buf, 16, 58, 14, LIGHTGRAY);
272 snprintf(buf,
sizeof(buf),
"B_max = %.3e T (rod+dipole)", (
double)B_max);
273 DrawText(buf, 16, 78, 14, LIGHTGRAY);
274 DrawText(
"LMB: orbit RMB: pan Scroll: zoom", 16, 100, 14, GRAY);
275 DrawText(
"Shift+LMB: move magnet (XZ + Y)", 16, 118, 14,
276 shift ? YELLOW : GRAY);
279 for (
int px = 0; px < 200; ++px) {
280 Color c = field_color(px / 199.0f);
281 DrawRectangle(W - 220 + px, H - 36, 1, 18, c);
283 DrawText(
"0", W - 224, H - 36, 14, LIGHTGRAY);
284 DrawText(
"B_max", W - 16, H - 36, 14, LIGHTGRAY);