2.9.0 release target; installed Windows x64 SDK evidence
Package applicability is under review; follow the stated version and platform limits.
Use the matching exported C interface from either language
The packaged C API provides opaque integer handles and extern C exports that can be called from C or C++. This worked example builds the same client as C11 and C++17, runs a complete RC deck and writes named transient data to a new CSV. It does not require the caller to instantiate JoSIM core C++ classes.
Use a matching 64-bit installation, public headers, C API import library, wrapper DLL and core DLL. The checked Windows 2.9 installation places jpro_capi.hpp, jpro_capi_errors.hpp and jpro_capi_export.hpp under include/josim-pro/capi. Its josim-pro_CAPI_AMD64.lib and josim-pro_CAPI_AMD64.dll are in bin beside josim-pro.dll. The core library lib/josim-pro.lib is a different link target.
Keep the calling-convention macros from the supplied header. Do not replace declarations with handwritten prototypes or pair a header from a different checkout with an installed DLL. JPRO_Util_GetCoreIdentity reports the core actually loaded, including its version, path, hash and C++ ABI facts. Latest-version/update metadata is a different question.
Both consumer builds in the recorded check loaded the exact installed 2.9.0 core. Direct use of C++ core classes needs its own matching compiler and standard-library ABI; this guide qualifies C++ through the exported C ABI. Final distributed packages and other platforms still need their own acceptance checks.
Download and build the complete example
This changes PATH only in the current shell. The checked build uses Windows x64 and the installed import library; it does not qualify another operating system, a 32-bit client or arbitrary toolchain combinations. An executable that builds successfully has not yet demonstrated successful DLL loading or licensed solving.
- Extract the ZIP into a new writable folder. Keep embed.c, CMakeLists.txt and rc_basic.cir together. The matching installed SDK is a prerequisite; this small teaching ZIP does not contain or install product DLLs.
- Open an x64 Native Tools developer PowerShell with CMake, Ninja and clang-cl available. Use the commands below from the extracted folder. If the installation is elsewhere, add -DJOSIM_ROOT="C:/path/to/JoSIM®-Pro" to the CMake configure command.
- Read configure/build diagnostics before running the clients. CMake checks the expected headers, C API import library and runtime files. Both clients need the matching bin directory on the child process DLL search path. Keep each CSV name new.
# Use an x64 Native Tools developer PowerShell with CMake, Ninja and clang-cl.
$env:PATH = "$env:ProgramFiles\SUN Magnetics\JoSIM-Pro\bin;$env:PATH"
cmake -S . -B build -G Ninja -DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl -DCMAKE_BUILD_TYPE=Release
if ($LASTEXITCODE -ne 0) { throw "CMake configure failed" }
cmake --build build
if ($LASTEXITCODE -ne 0) { throw "Client build failed" }
.\build\embed_c.exe .\rc_basic.cir .\rc_c.csv
if ($LASTEXITCODE -ne 0) { throw "C client failed; read its first diagnostic" }
.\build\embed_cpp.exe .\rc_basic.cir .\rc_cpp.csv
if ($LASTEXITCODE -ne 0) { throw "C++ client failed; read its first diagnostic" }
josim-pro-cli -o rc_cli.csv rc_basic.cir
if ($LASTEXITCODE -ne 0) { throw "CLI comparison run failed" }Follow the ownership and output sequence
String getters with outNeededSize use a two-call contract. The null/zero-capacity query returns kErrBufferTooSmall and the required byte count includes the terminating NUL. Allocate that many bytes, call again and check the new status. The example exercises this for core identity and each visible print name.
Handles are opaque identifiers, not object pointers. Passing an Input handle where Matrix is required, retaining stale handles or freeing internal arrays is not a shortcut. This example avoids creating a separate raw Results alias and uses formatted .PRINT arrays, so the known circuit supplies named voltage/current quantities.
JPRO_GetLastError returns this calling thread's current error text, and a successful API call may clear it. The example reports a failure immediately, before cleanup calls can replace its diagnostic. If your application needs the message later, copy it into its own storage before making another API call.
- Create Input from the circuit filename, create a Runtime, and build Matrix with JPRO_Matrix_CreateFromInputEx(input, runtime, ...). Check each returned status. Create and run Simulation, then check JPRO_Simulation_IsOK.
- Create Output from Input and Matrix. Format its time vector from Simulation, then compute all requested print values. Query the formatted time array, visible print names and visible print arrays.
- Validate lengths, finite values and the transient time axis before creating the CSV. The example writes 17 significant digits and preserves the packaged API print names, which may already contain CSV quoting.
- Consume borrowed array pointers while Output and its calculation objects remain alive and unmodified. To retain data beyond that lifetime, copy values into caller-owned storage first. Do not free a borrowed pointer with free or delete.
- Release caller-owned name buffers and destroy each successfully created handle through its matching API: Output, Simulation, Matrix, Runtime, then Input. The example checks destructor status too.
Check the result and its units
R1 = 50 ohms and C1 = 0.2 pF give a 10 ps time constant. For the 1 mV, 10 ps ramp, the independent ideal response is used separately from file/array checks. The recorded maximum output-voltage difference is approximately 0.556 microvolts, or 0.0556% of the drive. It passes the existing teaching check of less than 0.5% of the drive. This is a bounded linear-RC regression, not a universal solver tolerance or Josephson-physics validation.
The API client writes more digits than the checked CLI default. A byte-for-byte CSV comparison would confuse representation with calculation. The check first verifies headers and sample alignment, then rounds each API value to the observed CLI decimal format and requires equality. General accuracy, time-step refinement and other modes require appropriate independent references.
A successful API status, a newly created file and a plausible trace each provide different evidence. Check the current run status, finite named arrays, axis, units and expected circuit response together.
| Output | Expected for the supplied deck |
|---|---|
| time | 201 finite strictly increasing samples, 0 to 100 ps; stored in seconds |
| V(IN), V(OUT) | Volts; a 1 mV ramp from 10 to 20 ps and its RC response |
| I(R1) | Amperes in the declared resistor orientation |
| C versus C++ | Identical parsed CSV values and headers |
| API versus CLI | Identical after rounding API values to the CLI scientific format with six digits after the decimal point |
Recover from the first reported failure
The recorded checks reject a missing input with an IO status and a missing include with an API diagnostic; neither creates an output. They preserve an existing CSV sentinel and refuse to overwrite the source circuit. A bad destination directory fails explicitly. A corrected C++ run to a new CSV reproduces the successful result.
The first local attempt reached PRODUCT_NOT_LICENSED. Loading the already issued JoSIM licence through the supported broker command restored normal execution; the licence files and installed SDK were not replaced. No licence contents or holder information are included in the teaching files.
The A filename entry point expects UTF-8. This command-line client is checked with ASCII Windows paths; a Windows char** argument is not automatically UTF-8 for arbitrary user input. For broader Unicode paths, use the matching W entry point or an explicit, correctly checked conversion in your host application.
| Observed problem | Next action |
|---|---|
| Header or import-library error | Use the matching C API header directory and bin/josim-pro_CAPI_AMD64.lib; confirm the x64 developer environment and actual installation path. |
| Executable cannot load a DLL | Check the wrapper, core and their packaged runtime dependencies and architectures. Put the matching installed bin on the process DLL search path. A successful link does not prove runtime loading. |
| PRODUCT_NOT_LICENSED | Confirm an issued JoSIM entitlement is loaded by the licence daemon. An InductEx®/TTH grant does not establish a JoSIM grant. Use the normal licence manager or documented broker workflow for the existing issued file. |
| Input or include error | Read the named API call and immediate diagnostic, correct the saved circuit/dependency path, and rerun to a fresh CSV. |
| Cannot create NEW output | Use an existing writable destination directory and a filename that does not exist. The example deliberately refuses an existing output or input/output collision. |
| Incomplete CSV after a write/close error | Treat the run as failed. Preserve diagnostics and choose a new valid destination; file existence alone is insufficient. |
Choose the right API for a larger integration
This client intentionally covers one buffered transient deck. Explicit analysis jobs, AC/DC/OP, IV, testbenches, streaming/file-only modes, callbacks, cancellation and parallel applications have their own result and lifetime contracts. Do not infer those semantics from this single Run call.
Do not share or mutate the active handles concurrently based on this example. The supplied header contains operation-specific overlap rules; thread-local diagnostics alone do not make every API object concurrently usable. Keep callbacks alive for the documented period and avoid reentrant calls until their particular contract is established.
Use the complete checked client as a starting point, then add the required analysis/output route with its own success and recovery checks. Final-package acceptance, other platforms, broader Unicode command-line handling and native C++ class ABI remain open. This guide and its download remain unpublished drafts.
Complete embed.c
/* Build this same client as C11 or C++17 against the matching JoSIM C API. */
#include "jpro_capi.hpp"
#include <errno.h>
#include <inttypes.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Trace {
char *name; /* Caller-owned string buffer. */
double *values; /* Borrowed: never free or use after Output changes. */
int64_t length;
int hidden;
} Trace;
static void report(const char *call, int code) {
/* Successful API calls may clear this thread's last error. Read it first. */
const char *message = JPRO_GetLastError();
fprintf(stderr, "%s failed (%d): %s\n", call, code, message ? message : "");
}
static int get_name(int output, int index, char **name) {
int needed = 0;
int code = JPRO_Output_GetPrintName(output, index, &needed, NULL, 0);
if (code != kErrBufferTooSmall || needed < 1 || needed > 65536) {
report("JPRO_Output_GetPrintName(size)", code);
return 0;
}
*name = (char *)malloc((size_t)needed);
if (!*name) { fprintf(stderr, "Cannot allocate print name\n"); return 0; }
code = JPRO_Output_GetPrintName(output, index, &needed, *name, needed);
if (code != kErrOk) { report("JPRO_Output_GetPrintName(value)", code); return 0; }
return 1;
}
static void csv_name(FILE *file, const char *name) {
size_t length = strlen(name);
/* This package's print names may already carry complete CSV quoting. */
if (length >= 2 && name[0] == '"' && name[length - 1] == '"') {
fputs(name, file);
return;
}
fputc('"', file);
while (*name) {
if (*name == '"') { fputc('"', file); }
fputc(*name++, file);
}
fputc('"', file);
}
#define CHECK(call) do { code = (call); if (code != kErrOk) { \
report(#call, code); goto cleanup; } } while (0)
#define DESTROY(handle, function) do { if ((handle) != 0) { \
code = function(handle); (handle) = 0; \
if (code != kErrOk) { report(#function, code); result = 1; } } } while (0)
int main(int argc, char **argv) {
int input = 0, runtime = 0, matrix = 0, simulation = 0, output = 0;
int code = kErrOk, result = 1, ok = 0, count = 0, visible = 0, i = 0;
int needed = 0;
int64_t rows = 0, row = 0;
double *time = NULL; /* Borrowed from Output while it stays alive. */
Trace *traces = NULL;
char *identity = NULL;
FILE *file = NULL;
if (argc != 3) {
fprintf(stderr, "Usage: %s circuit.cir NEW-output.csv\n", argv[0]);
return 2;
}
/* This utility identifies the core actually loaded, not a latest-release URL. */
code = JPRO_Util_GetCoreIdentity(&needed, NULL, 0);
if (code != kErrBufferTooSmall || needed < 1 || needed > 65536) {
report("JPRO_Util_GetCoreIdentity(size)", code); goto cleanup;
}
identity = (char *)malloc((size_t)needed);
if (!identity) { fprintf(stderr, "Cannot allocate core identity\n"); goto cleanup; }
CHECK(JPRO_Util_GetCoreIdentity(&needed, identity, needed));
printf("CORE_IDENTITY=%s\n", identity);
CHECK(JPRO_Input_CreateFromFileA(argv[1], &input));
CHECK(JPRO_Runtime_Create(&runtime));
CHECK(JPRO_Matrix_CreateFromInputEx(input, runtime, &matrix));
CHECK(JPRO_Simulation_CreateFromMatrix(matrix, &simulation));
CHECK(JPRO_Simulation_Run(simulation, matrix));
CHECK(JPRO_Simulation_IsOK(simulation, &ok));
if (!ok) { fprintf(stderr, "Simulation did not report success\n"); goto cleanup; }
CHECK(JPRO_Output_CreateFromInputMatrix(input, matrix, &output));
CHECK(JPRO_Output_FormatTimeVector(output, simulation, matrix));
CHECK(JPRO_Output_ComputeAllPrints(output, matrix));
CHECK(JPRO_Output_GetTimeVector(output, &time, &rows));
CHECK(JPRO_Output_GetPrintCount(output, &count));
if (rows < 1 || !time || count < 1 || count > 10000) {
fprintf(stderr, "No usable formatted output\n"); goto cleanup;
}
traces = (Trace *)calloc((size_t)count, sizeof(Trace));
if (!traces) { fprintf(stderr, "Cannot allocate trace descriptors\n"); goto cleanup; }
for (i = 0; i < count; ++i) {
CHECK(JPRO_Output_GetPrintHidden(output, i, &traces[i].hidden));
if (traces[i].hidden) { continue; }
if (!get_name(output, i, &traces[i].name)) { goto cleanup; }
CHECK(JPRO_Output_GetPrintValues(output, i, &traces[i].values, &traces[i].length));
if (!traces[i].values || traces[i].length != rows) {
fprintf(stderr, "Trace length does not match formatted time\n"); goto cleanup;
}
++visible;
}
if (!visible) { fprintf(stderr, "No visible .PRINT traces\n"); goto cleanup; }
for (row = 0; row < rows; ++row) {
if (!isfinite(time[row]) || (row > 0 && time[row] <= time[row - 1])) {
fprintf(stderr, "Invalid transient time axis\n"); goto cleanup;
}
for (i = 0; i < count; ++i) {
if (!traces[i].hidden && !isfinite(traces[i].values[row])) {
fprintf(stderr, "Nonfinite output value\n"); goto cleanup;
}
}
}
/* Exclusive creation also protects the input if the output names that file. */
file = fopen(argv[2], "wx");
if (!file) {
fprintf(stderr, "Cannot create NEW output (%d): %s\n", errno, strerror(errno));
goto cleanup;
}
fputs("time", file);
for (i = 0; i < count; ++i) {
if (!traces[i].hidden) { fputc(',', file); csv_name(file, traces[i].name); }
}
fputc('\n', file);
for (row = 0; row < rows; ++row) {
fprintf(file, "%.17g", time[row]);
for (i = 0; i < count; ++i) {
if (!traces[i].hidden) { fprintf(file, ",%.17g", traces[i].values[row]); }
}
fputc('\n', file);
}
if (ferror(file)) { fprintf(stderr, "CSV write failed; output is incomplete\n"); goto cleanup; }
if (fclose(file) != 0) { file = NULL; fprintf(stderr, "CSV close failed\n"); goto cleanup; }
file = NULL;
printf("WROTE rows=%" PRId64 " traces=%d\n", rows, visible);
result = 0;
cleanup:
if (file) { fclose(file); } /* A write failure may leave a partial file. */
if (traces) { for (i = 0; i < count; ++i) { free(traces[i].name); } }
free(traces); free(identity);
/* Borrowed array pointers are no longer used below this point. */
DESTROY(output, JPRO_Output_Destroy);
DESTROY(simulation, JPRO_Simulation_Destroy);
DESTROY(matrix, JPRO_Matrix_Destroy);
DESTROY(runtime, JPRO_Runtime_Destroy);
DESTROY(input, JPRO_Input_Destroy);
return result;
}
Complete CMakeLists.txt
cmake_minimum_required(VERSION 3.20)
project(josim_embed_example LANGUAGES C CXX)
if(NOT WIN32 OR NOT CMAKE_SIZEOF_VOID_P EQUAL 8)
message(FATAL_ERROR "This checked build recipe targets Windows x64.")
endif()
set(JOSIM_ROOT "C:/Program Files/SUN Magnetics/JoSIM-Pro" CACHE PATH "Matching JoSIM 2.9 installation")
set(CAPI_DIR "${JOSIM_ROOT}/bin")
foreach(required
"${JOSIM_ROOT}/include/josim-pro/capi/jpro_capi.hpp"
"${CAPI_DIR}/josim-pro_CAPI_AMD64.lib"
"${CAPI_DIR}/josim-pro_CAPI_AMD64.dll"
"${CAPI_DIR}/josim-pro.dll")
if(NOT EXISTS "${required}")
message(FATAL_ERROR "Missing matching installed SDK file: ${required}")
endif()
endforeach()
add_library(JoSIM_CAPI SHARED IMPORTED)
set_target_properties(JoSIM_CAPI PROPERTIES
IMPORTED_IMPLIB "${CAPI_DIR}/josim-pro_CAPI_AMD64.lib"
IMPORTED_LOCATION "${CAPI_DIR}/josim-pro_CAPI_AMD64.dll"
INTERFACE_INCLUDE_DIRECTORIES "${JOSIM_ROOT}/include/josim-pro/capi")
add_executable(embed_c embed.c)
set_target_properties(embed_c PROPERTIES C_STANDARD 11 C_STANDARD_REQUIRED YES)
configure_file(embed.c "${CMAKE_CURRENT_BINARY_DIR}/embed.cpp" COPYONLY)
add_executable(embed_cpp "${CMAKE_CURRENT_BINARY_DIR}/embed.cpp")
set_target_properties(embed_cpp PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED YES)
target_link_libraries(embed_c PRIVATE JoSIM_CAPI)
target_link_libraries(embed_cpp PRIVATE JoSIM_CAPI)
Complete rc_basic.cir
* RC response: 50 ohm, 0.2 pF, 10 ps time constant
.PARAM DRIVE=1m
V1 IN 0 PWL(0 0 10p 0 20p DRIVE 100p DRIVE)
R1 IN OUT 50
C1 OUT 0 0.2p
.OPTION ANALYSIS=VOLTAGE INTEGRATION=BDF
.TRAN 0.5p 100p
.PRINT V(IN) V(OUT) I(R1)
.END
Sources and verification details
Recorded inputs/builds only. Complete walkthrough and final-package acceptance remain separate.
VAL-029 · Installed Windows x64 JoSIM®-Pro 2.9.0 review core and matching packaged C API components; clang-cl consumer builds, 3 November 2026
Real Windows x64 C11 and C++17 consumers of the installed JoSIM 2.9 C ABI: matched loaded-core identity, formatted transient arrays, NUL-inclusive two-call strings, reverse owned-handle cleanup, exclusive CSV creation and error/recovery paths
Seventeen checks cover one complete buffered linear-RC transient and its stated failures. Native C++ class ABI, other analyses, jobs/sweeps, streaming, callbacks, cancellation, concurrent ownership, arbitrary Unicode command-line paths, other platforms and final distributed SDK/release acceptance remain pending. CLI decimal rounding and the existing bounded RC reference check are separate from general physical accuracy.
- installed:josim-capi-sdk-2.9-20261103 · Packaged C API declarations, error codes, calling convention and exact SDK identities
- validation:josim-capi-windows-20261103 · VAL-029; complete compiled client, normal licensed execution, output checks and recovery
- Seventeen checks cover one complete buffered linear-RC transient and its stated failures. Native C++ class ABI, other analyses, jobs/sweeps, streaming, callbacks, cancellation, concurrent ownership, arbitrary Unicode command-line paths, other platforms and final distributed SDK/release acceptance remain pending. CLI decimal rounding and the existing bounded RC reference check are separate from general physical accuracy.
- The first licence rejection remains retained in private logs. A supported load of the existing issued JoSIM licence restored the real runtime checks. No product source, SDK or licence-file edit was made.
