diff --git a/ffi/femm_mag.cpp b/ffi/femm_mag.cpp new file mode 100644 index 0000000..ae30d09 --- /dev/null +++ b/ffi/femm_mag.cpp @@ -0,0 +1,141 @@ +// magnetostatic FFI body: opaque doc, pipeline, mesh accessors. delegates to fkn's CFemmeDocCore. + +#include "../fkn/StdAfx.h" +#include "../fkn/fknDlg.h" +#include "../fkn/complex.h" +#include "../fkn/spars.h" +#include "../fkn/mesh.h" +#include "../fkn/FemmeDocCore.h" + +#include "femm_mag.h" + +#include +#include +#include + +// stub view: math files reference TheView globally, math methods call SetPos/SetDlgItemText/InvalidateRect on it. +static CFknDlg s_stub_view; +CFknDlg* TheView = &s_stub_view; + +// math files declare and call these for error reporting. +int MsgBox(const char* fmt, ...) { + va_list ap; va_start(ap, fmt); + std::vfprintf(stderr, fmt, ap); + std::fputc('\n', stderr); + va_end(ap); + return 0; +} +int MsgBox(const std::string& s) { + std::fprintf(stderr, "%s\n", s.c_str()); + return 0; +} + +// referenced by fkn/main.cpp's old_main wait loop; main.cpp itself is not linked here. +inline bool IsWindow(void*) { return true; } + +struct FemmMagDoc { + CFemmeDocCore doc; + FemmMagProgressFn cb = nullptr; + void* user = nullptr; + std::string path_buf; +}; + +extern "C" { + +FemmMagDoc* femm_mag_doc_new(void) { + auto* d = new FemmMagDoc(); + d->doc.TheView = &s_stub_view; + return d; +} + +void femm_mag_doc_free(FemmMagDoc* d) { + delete d; +} + +void femm_mag_doc_set_progress(FemmMagDoc* d, FemmMagProgressFn fn, void* user) { + if (!d) return; + d->cb = fn; + d->user = user; +} + +int femm_mag_doc_load_fem(FemmMagDoc* d, const char* path) { + if (!d || !path) return 0; + d->path_buf = path; + d->doc.PathName = const_cast(d->path_buf.c_str()); + return d->doc.OnOpenDocument() ? 1 : 0; +} + +int femm_mag_doc_load_mesh(FemmMagDoc* d) { + return (d && d->doc.LoadMesh()) ? 1 : 0; +} + +int femm_mag_doc_renumber(FemmMagDoc* d) { + return (d && d->doc.Cuthill()) ? 1 : 0; +} + +// dispatches on Frequency (DC vs AC) and ProblemType (planar vs axisymmetric). +int femm_mag_doc_solve(FemmMagDoc* d) { + if (!d) return 0; + if (d->doc.Frequency == 0) { + CBigLinProb L; + L.TheView = &s_stub_view; + L.Precision = d->doc.Precision; + if (!L.Create(d->doc.NumNodes, d->doc.BandWidth)) return 0; + if (d->doc.ProblemType == FALSE) { + if (!d->doc.Static2D(L)) return 0; + } else { + if (!d->doc.StaticAxisymmetric(L)) return 0; + } + if (!d->doc.WriteStatic2D(L)) return 0; + } else { + CBigComplexLinProb L; + L.TheView = &s_stub_view; + L.Precision = d->doc.Precision; + if (!L.Create(d->doc.NumNodes + d->doc.NumCircProps, d->doc.BandWidth, d->doc.NumNodes)) return 0; + if (d->doc.ProblemType == FALSE) { + if (!d->doc.Harmonic2D(L)) return 0; + } else { + if (!d->doc.HarmonicAxisymmetric(L)) return 0; + } + if (!d->doc.WriteHarmonic2D(L)) return 0; + } + return 1; +} + +int femm_mag_doc_write_results(FemmMagDoc* /*d*/, const char* /*out_path*/) { + // results currently emitted inline by solve(); reserved hook for explicit output redirection. + return 1; +} + +double femm_mag_doc_frequency (const FemmMagDoc* d) { return d ? d->doc.Frequency : 0.0; } +int femm_mag_doc_axisymmetric (const FemmMagDoc* d) { return (d && d->doc.ProblemType) ? 1 : 0; } +double femm_mag_doc_depth (const FemmMagDoc* d) { return d ? d->doc.extZo : 0.0; } +double femm_mag_doc_precision (const FemmMagDoc* d) { return d ? d->doc.Precision : 0.0; } + +int femm_mag_doc_num_nodes (const FemmMagDoc* d) { return d ? d->doc.NumNodes : 0; } + +void femm_mag_doc_node (const FemmMagDoc* d, int i, double* x, double* y) { + if (!d || !d->doc.meshnode) return; + if (x) *x = d->doc.meshnode[i].x; + if (y) *y = d->doc.meshnode[i].y; +} + +int femm_mag_doc_num_elements (const FemmMagDoc* d) { return d ? d->doc.NumEls : 0; } + +void femm_mag_doc_element (const FemmMagDoc* d, int i, int* p0, int* p1, int* p2) { + if (!d || !d->doc.meshele) return; + if (p0) *p0 = d->doc.meshele[i].p[0]; + if (p1) *p1 = d->doc.meshele[i].p[1]; + if (p2) *p2 = d->doc.meshele[i].p[2]; +} + +int femm_mag_doc_num_materials (const FemmMagDoc* d) { return d ? d->doc.NumBlockProps : 0; } +int femm_mag_doc_num_boundaries (const FemmMagDoc* d) { return d ? d->doc.NumLineProps : 0; } +int femm_mag_doc_num_circuits (const FemmMagDoc* d) { return d ? d->doc.NumCircProps : 0; } + +// field sampling is post-processor territory; not exposed by the solver alone. +double femm_mag_doc_field_at (const FemmMagDoc* /*d*/, double /*x*/, double /*y*/, FemmMagField /*c*/) { + return 0.0; +} + +} // extern "C" diff --git a/ffi/femm_mag.h b/ffi/femm_mag.h new file mode 100644 index 0000000..f995f1c --- /dev/null +++ b/ffi/femm_mag.h @@ -0,0 +1,63 @@ +// C ABI for the magnetostatic solver (fkn): opaque doc, pipeline, mesh accessors, progress callback. +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct FemmMagDoc FemmMagDoc; + +// pct in 0..100, or -1 for label-only updates. label may be NULL. +typedef void (*FemmMagProgressFn)(int pct, const char* label, void* user); + +// lifetime +FemmMagDoc* femm_mag_doc_new(void); +void femm_mag_doc_free(FemmMagDoc* doc); + +// progress reporting routed to the host (Rust/Iced). +void femm_mag_doc_set_progress(FemmMagDoc* doc, FemmMagProgressFn fn, void* user); + +// pipeline. each returns 1 on success, 0 on failure. +int femm_mag_doc_load_fem (FemmMagDoc* doc, const char* path); +int femm_mag_doc_load_mesh (FemmMagDoc* doc); +int femm_mag_doc_renumber (FemmMagDoc* doc); +int femm_mag_doc_solve (FemmMagDoc* doc); +int femm_mag_doc_write_results (FemmMagDoc* doc, const char* out_path); + +// problem attributes after load_fem. +double femm_mag_doc_frequency (const FemmMagDoc* doc); +int femm_mag_doc_axisymmetric (const FemmMagDoc* doc); +double femm_mag_doc_depth (const FemmMagDoc* doc); +double femm_mag_doc_precision (const FemmMagDoc* doc); + +// mesh, valid after load_mesh + renumber. +int femm_mag_doc_num_nodes (const FemmMagDoc* doc); +void femm_mag_doc_node (const FemmMagDoc* doc, int i, double* x, double* y); +int femm_mag_doc_num_elements (const FemmMagDoc* doc); +void femm_mag_doc_element (const FemmMagDoc* doc, int i, int* p0, int* p1, int* p2); + +// property table sizes. names live in the .fem parser / material library, not the solver runtime; +// per-block properties (mu_x, BdryFormat, etc.) get their own accessors when the GUI needs them. +int femm_mag_doc_num_materials (const FemmMagDoc* doc); +int femm_mag_doc_num_boundaries (const FemmMagDoc* doc); +int femm_mag_doc_num_circuits (const FemmMagDoc* doc); + +// field-point sampling after solve. component selects which scalar to fetch. +typedef enum { + FEMM_MAG_FIELD_A_RE = 0, + FEMM_MAG_FIELD_A_IM = 1, + FEMM_MAG_FIELD_BX_RE = 2, + FEMM_MAG_FIELD_BX_IM = 3, + FEMM_MAG_FIELD_BY_RE = 4, + FEMM_MAG_FIELD_BY_IM = 5, + FEMM_MAG_FIELD_B_MAG_RE = 6, + FEMM_MAG_FIELD_B_MAG_IM = 7, +} FemmMagField; + +double femm_mag_doc_field_at (const FemmMagDoc* doc, double x, double y, FemmMagField component); + +#ifdef __cplusplus +} +#endif