initial commit
authorametama <ametama@wafflesoft.org>
Fri, 30 Jan 2026 22:59:14 +0000 (23:59 +0100)
committerametama <ametama@wafflesoft.org>
Fri, 30 Jan 2026 22:59:14 +0000 (23:59 +0100)
.gitignore [new file with mode: 0644]
Makefile [new file with mode: 0644]
include/argph.h [new file with mode: 0644]
include/def.h [new file with mode: 0644]
src/argp.c [new file with mode: 0644]
src/main.c [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..4c04976
--- /dev/null
@@ -0,0 +1,4 @@
+compile_flags.txt
+ngsolve
+test.c
+test.ngs
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..719aad9
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,15 @@
+build: compile_flags executable
+
+#compile_flags_gtk:
+#      echo "-Iinclude `pkg-config --cflags gtk4` -lm -fopenmp `pkg-config --libs gtk4`" | perl -pe 's|(-.*?)\s|\1\n|g' > compile_flags.txt
+#      echo "-o cgo" >> compile_flags.txt
+
+compile_flags:
+       echo "-Iinclude -lcgo -lm -fopenmp" | perl -pe 's|(-.*?)\s|\1\n|g' > compile_flags.txt
+
+executable: $(wildcard src/*.c)
+       gcc $(shell cat compile_flags.txt) -o ngsolve $(shell ls src/*.c)
+
+clear:
+       rm -f compile_flags.txt
+       rm -f extspice
diff --git a/include/argph.h b/include/argph.h
new file mode 100644 (file)
index 0000000..b574471
--- /dev/null
@@ -0,0 +1,4 @@
+#include <argp.h>
+
+struct arguments {};
+static struct argp argp;
diff --git a/include/def.h b/include/def.h
new file mode 100644 (file)
index 0000000..7cd100c
--- /dev/null
@@ -0,0 +1,20 @@
+typedef struct {
+    char *symbol;
+    double sample_min;
+    double sample_max;
+} design_var;
+
+typedef struct {
+    char *symbol;
+    char *expr;
+} measure_var;
+
+typedef struct {
+    char *netlist_file;
+    char *netlist;
+    int agentc;
+    int dvarc;
+    int mvarc;
+    design_var *dvars;
+    measure_var *mvars;
+} task_def;
diff --git a/src/argp.c b/src/argp.c
new file mode 100644 (file)
index 0000000..54f7870
--- /dev/null
@@ -0,0 +1,17 @@
+#include "argph.h"
+
+static char doc[] = "ngsolve: ngspice optimizer";
+static char args_doc[] = "FILE";
+static struct argp_option options[] = {};
+static error_t parse_opt(int key, char *arg, struct argp_state *state) {
+    struct arguments *args = state->input;
+    switch (key) {
+        case ARGP_KEY_END:
+            if (state->arg_num > 0) argp_usage(state);
+            break;
+        default:
+            return ARGP_ERR_UNKNOWN;
+    }
+    return 0;
+}
+static struct argp argp = { options, parse_opt, args_doc, doc };
diff --git a/src/main.c b/src/main.c
new file mode 100644 (file)
index 0000000..ecc6bb2
--- /dev/null
@@ -0,0 +1,98 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <cgo.h>
+#include "argph.h"
+#include "def.h"
+
+typedef struct {
+    pid_t pid;
+    int input_read;
+    int input_write;
+    int output_read;
+    int output_write;
+} child_process;
+
+task_def task;
+
+char *readfile(FILE *f) {
+    if (f == NULL) return NULL;
+    int c;
+    char *str;
+    fseek(f, 0, SEEK_END);
+    long f_size = ftell(f);
+    fseek(f, 0, SEEK_SET);
+    str = malloc(f_size + 1);
+    for (size_t i = 0; i < f_size; i++) str[i] = (char) fgetc(f);
+    str[f_size] = '\0';
+    return str;
+}
+
+void spawn_ngspice(child_process *chp) {
+    if (pipe(&chp->input_read) || pipe(&chp->output_read)) {
+        printf("ngsolve: FATAL EXCEPTION\n    pipe creation unsuccessful.");
+        exit(1);
+    }
+    pid_t p = fork();
+    if (!p) {
+        close(chp->input_write);  // Used by parent.
+        close(chp->output_read);  // Used by parent.
+        // Connect stdin to pipe in parent.
+        dup2(chp->input_read, STDIN_FILENO);
+        // Convert to an ngspice process;
+        // execlp does not (!) return to caller.
+        if (execlp("ngspice", "ngspice", "<&0", NULL)) {
+            printf("ngsolve: FATAL EXCEPTION\n    ngspice invocation unsuccessful. Check that a working binary is added to PATH as `ngspice`.");
+            exit(1);
+        }
+    }
+    close(chp->input_read);  // Used by child.
+    close(chp->output_write);  // Used by child.
+    chp->pid = p;
+}
+
+double fitness(double *x) {
+    child_process chp;
+    spawn_ngspice(&chp);
+    dprintf(chp.input_write, "ngsolve\n");
+    for (size_t d = 0; d < task.dvarc; d++)
+        dprintf(chp.input_write, ".PARAM %s=%f\n", task.dvars[d].symbol, x[d]);
+    dprintf(chp.input_write, "%s\n.CONTROL\necho \"", task.netlist);
+    for (size_t d = 0; d < task.mvarc; d++)
+        dprintf(chp.input_write, "$&%s ", task.mvars[d].symbol);
+    dprintf(chp.input_write, "\" >&%d\nexec %d>&-\n.ENDC\n.END\n" /* Done writing (writes EOF to parent on chp.output_write). */, chp.output_write, chp.output_write);
+    close(chp.input_write);  // Done writing (writes EOF to ngspice on chp.input_write).
+    // TODO: read measurements and build fitness from constraints and objectives.
+}
+
+void optimize() {
+    double *sample_range_min = malloc(sizeof(double) * task.dvarc);
+    double *sample_range_max = malloc(sizeof(double) * task.dvarc);
+    for (size_t d = 0; d < task.dvarc; d++) {
+        sample_range_min[d] = task.dvars[d].sample_min;
+        sample_range_max[d] = task.dvars[d].sample_max;
+    }
+    FILE *netlist = fopen(task.netlist_file, "r");
+    task.netlist = readfile(netlist);
+    fclose(netlist);
+    cgo_de_parameters par = {
+        task.dvarc,
+        task.agentc,
+        sample_range_min,
+        sample_range_max,
+        fitness,
+        0.9,
+        0.8
+    };
+    double *res = malloc(sizeof(double) * task.dvarc);
+    cgo_de(&par, res);
+    printf("ngsolve: converged at x=(");
+    for (size_t d = 0; d < task.dvarc - 1; d++) printf("%s=%f, ", task.dvars[d].symbol, res[d]);
+    printf("%s=%f) with f(x)=%f\n", task.dvars[task.dvarc - 1].symbol, res[task.dvarc - 1], fitness(res));
+}
+
+int main(int argc, char *argv[]) {
+    struct arguments args;
+    argp_parse(&argp, argc, argv, 0, 0, &args);
+    return 0;
+}