diff --git a/README.md b/README.md index 8e350c2..8b86a51 100644 --- a/README.md +++ b/README.md @@ -115,26 +115,54 @@ The DSL is compiled to a WASM module using the `compileModel` function: import { compileModel } from 'diffeq-js'; const code = `...`; -compileModel(code).then(() => { - // create Solver, Vector etc here -}); +// Compile with an optional ID to support multiple models +await compileModel(code, 'model1'); ``` The `compileModel` function requires an active internet connection as it sends the model code to a remote server for compilation to WASM. All the classes in the library are wrappers around corresponding classes in the WASM module, and so the `compileModel` function must be called successfully before any of the other -classes can be used. +classes can be used. + +### Multiple Models + +Version 0.2.0 introduces support for compiling and solving multiple models simultaneously. Each model can be given a unique identifier when compiled: + +```javascript +// Compile multiple models +await compileModel(code1, 'model1'); +await compileModel(code2, 'model2'); + +// Create solvers for each model +const options1 = new Options({}, 'model1'); +const solver1 = new Solver(options1, 'model1'); + +const options2 = new Options({}, 'model2'); +const solver2 = new Solver(options2, 'model2'); +``` ### Options -The `compileModel` function takes an `Options` object as its argument. The contructor for the `Options` class takes the following arguments: +The `Options` class constructor takes an options object and an optional model ID: -* `print_stats` - statistics about each solve are printed to the console after each successful call to `solve`. Default: false -* `fixed_times` - if false (the default), the solver will consider the first element - of `times` to be the starting time point, and the second element to be the final time point, - between these two times the solver will choose the time points to output (these are returned in the `times` vector). - If true, the solver will only return solutions at the times specified in the input `times` vector. Default: false +```javascript +const options = new Options({ + print_stats: false, // Print statistics after each solve + fixed_times: false, // Use fixed time points or let solver choose + mxsteps: 500, // Maximum number of steps + min_step: 0.0, // Minimum step size + max_step: Infinity, // Maximum step size + atol: 1e-6, // Absolute tolerance + rtol: 1e-6, // Relative tolerance + debug: false, // Enable debug output + fwd_sens: false, // Enable forward sensitivity analysis + linear_solver: OptionsLinearSolver.DENSE, // Linear solver type + preconditioner: OptionsPreconditioner.NONE, // Preconditioner type + jacobian: OptionsJacobian.DENSE_JACOBIAN, // Jacobian type + linsol_max_iterations: 100 // Maximum iterations for linear solver +}, 'model1'); +``` ### Compilation errors diff --git a/package.json b/package.json index 6facb6d..1c36c6a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@martinjrobins/diffeq-js", - "version": "0.1.6", + "version": "0.2.0", "homepage": "https://github.com/martinjrobins/diffeq-js", "description": "A library for solving differential equations in JavaScript", "author": "Martin Robinson (https://github.com/martinjrobins)", @@ -13,41 +13,37 @@ "url": "https://github.com/martinjrobins/diffeq-js" }, "source": "src/index.ts", - "main": "dist/main.js", - "types": "dist/types.d.ts", + "main": "./dist/diffeq-js.umd.js", + "module": "./dist/diffeq-js.es.js", + "types": "./dist/types.d.ts", "license": "MIT", - "browser": "dist/browser.js", + "browser": "./dist/diffeq-js.es.js", "type": "module", - "targets": { - "browser": { - "context": "browser", - "optimize": true, - "includeNodeModules": true, - "sourceMap": true, - "outputFormat": "esmodule" + "exports": { + ".": { + "import": "./dist/diffeq-js.es.js", + "require": "./dist/diffeq-js.umd.js", + "types": "./dist/types.d.ts" } }, - "@parcel/resolver-default": { - "packageExports": true - }, "scripts": { - "watch": "parcel watch", - "build": "parcel build", + "dev": "vite", + "build": "vite build", "test": "mocha -r ts-node/register" }, "devDependencies": { - "@parcel/packager-ts": "2.12.0", - "@parcel/transformer-typescript-types": "2.12.0", "@types/chai": "^4.3.5", "@types/mocha": "^10.0.1", "@types/node": "^20.5.9", + "@vitejs/plugin-vue": "^5.0.0", "chai": "^4.3.8", "mocha": "^10.2.0", "nyc": "^15.1.0", - "parcel": "^2.12.0", "ts-node": "^10.9.1", "tsconfig-paths": "^4.2.0", - "typescript": "^5.2.2" + "typescript": "^5.2.2", + "vite": "^5.0.0", + "vue-tsc": "^1.8.0" }, "dependencies": { "@bjorn3/browser_wasi_shim": "^0.2.14" diff --git a/src/index.ts b/src/index.ts index 2559e6b..cb51e28 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,24 +1,12 @@ -import { WASI, File, OpenFile, PreopenDirectory } from "@bjorn3/browser_wasi_shim"; -import { extract_vector_functions } from "./vector"; -import { extract_options_functions } from "./options"; -import { extract_solver_functions } from "./solver"; -import base from "/node_modules/base-x/src/index"; +import {File, OpenFile, WASI} from "@bjorn3/browser_wasi_shim"; +import {extract_vector_functions} from "./vector"; +import {extract_options_functions} from "./options"; +import {extract_solver_functions} from "./solver"; export { default as Vector } from "./vector"; export { default as Options, OptionsJacobian, OptionsLinearSolver, OptionsPreconditioner } from "./options"; export { default as Solver } from "./solver"; - -let args: string[] = []; -let env: string[] = []; -let fds = [ - new OpenFile(new File([])), // stdin - new OpenFile(new File([])), // stdout - new OpenFile(new File([])), // stderr -]; -let wasi = new WASI(args, env, fds); -let inst: WebAssembly.WebAssemblyInstantiatedSource | undefined = undefined; - class SimpleOpenFile { file: File; file_pos: number; @@ -32,29 +20,83 @@ class SimpleOpenFile { const end = data.byteLength; const slice = data.slice(start, end); this.file_pos = end; - const string = new TextDecoder().decode(slice); - return string; + return new TextDecoder().decode(slice); } } -// @ts-expect-error -let stderr = new SimpleOpenFile(wasi.fds[2].file); +const defaultBaseUrl = "https://compbio.fhs.um.edu.mo/diffeq"; -// @ts-expect-error -let stdout = new SimpleOpenFile(wasi.fds[1].file); +function createWasi() { + let args: string[] = []; + let env: string[] = []; + let fds = [ + new OpenFile(new File([])), // stdin + new OpenFile(new File([])), // stdout + new OpenFile(new File([])), // stderr + ]; + return new WASI(args, env, fds); +} -function getWasmMemory() { - if (inst === undefined) { - throw new Error("WASM module not loaded"); +class ModelRegistry { + private static models: Map = new Map(); + private static defaultModel: any = null; + + static register(id: string | undefined, model: any) { + if (id) { + this.models.set(id, model); + } else { + this.defaultModel = model; + } + } + + static get(id?: string) { + if (id) { + return this.models.get(id); + } + return this.defaultModel; } - return inst.instance.exports.memory as WebAssembly.Memory; } -const defaultBaseUrl = "https://diffeq-backend.fly.dev"; +function compileResponse(response: Promise) { + const wasi = createWasi(); + const importObject = { + "wasi_snapshot_preview1": wasi.wasiImport, + }; + return WebAssembly.instantiateStreaming(response, importObject).then( + (obj) => { + wasi.initialize({ + exports: { + memory: obj.instance.exports.memory as WebAssembly.Memory, + }, + }); + const stderr = new SimpleOpenFile((wasi.fds[2] as OpenFile).file); + const stdout = new SimpleOpenFile((wasi.fds[1] as OpenFile).file); + const vectorFunctions = extract_vector_functions(obj); + const optionsFunctions = extract_options_functions(obj); + const solverFunctions = extract_solver_functions(obj); + + // Set global functions + global.vectorFunctions = vectorFunctions; + global.optionsFunctions = optionsFunctions; + global.solverFunctions = solverFunctions; + global.stderr = stderr; + global.stdout = stdout; + + return { + instance: obj, + stderr, + stdout, + vectorFunctions, + optionsFunctions, + solverFunctions + }; + }, + ); +} -function compileModel(text: string, baseUrl: string = defaultBaseUrl) { +export async function compileModel(code: string, id?: string) { const data = { - text, + text: code, name: "unknown", }; const options: RequestInit = { @@ -65,7 +107,7 @@ function compileModel(text: string, baseUrl: string = defaultBaseUrl) { }, body: JSON.stringify(data), }; - const response = fetch(`${baseUrl}/compile`, options).then((response) => { + const response = fetch(`${defaultBaseUrl}/compile`, options).then((response) => { if (!response.ok) { return response.text().then((text) => { throw text; @@ -73,27 +115,15 @@ function compileModel(text: string, baseUrl: string = defaultBaseUrl) { } return response; }); - return compileResponse(response); + const model = await compileResponse(response); + ModelRegistry.register(id, model); + return model; } -function compileResponse(response: Promise) { - const importObject = { - "wasi_snapshot_preview1": wasi.wasiImport, - }; - return WebAssembly.instantiateStreaming(response, importObject).then( - (obj) => { - extract_vector_functions(obj); - extract_options_functions(obj); - extract_solver_functions(obj); - inst = obj - // @ts-expect-error - wasi.initialize(inst.instance); - }, - ); +export function getModel(id?: string) { + return ModelRegistry.get(id); } -export { compileModel, compileResponse, getWasmMemory, stderr, stdout } - diff --git a/src/options.ts b/src/options.ts index 5467c1b..2121b18 100644 --- a/src/options.ts +++ b/src/options.ts @@ -1,92 +1,117 @@ -import { check_function } from "./utils"; +import {check_function} from "./utils"; +import {File} from "@bjorn3/browser_wasi_shim"; +import {getModel} from "./index"; + +declare global { + var optionsFunctions: OptionsFunctions; + var stderr: SimpleOpenFile; + var stdout: SimpleOpenFile; +} type Options_create_t = () => number; -let Options_create: Options_create_t | undefined = undefined; type Options_destroy_t = (ptr: number) => void; -let Options_destroy: Options_destroy_t | undefined = undefined; -type Options_set_fixed_times_t = (ptr: number, fixed_times: number) => void; -let Options_set_fixed_times: Options_set_fixed_times_t | undefined = undefined; -type Options_set_print_stats_t = (ptr: number, print_stats: number) => void; -let Options_set_print_stats: Options_set_print_stats_t | undefined = undefined; -type Options_set_fwd_sens_t = (ptr: number, fwd_sens: number) => void; -let Options_set_fwd_sens: Options_set_fwd_sens_t | undefined = undefined; +type Options_set_fixed_times_t = (ptr: number, value: number) => void; +type Options_set_print_stats_t = (ptr: number, value: number) => void; +type Options_set_fwd_sens_t = (ptr: number, value: number) => void; +type Options_set_atol_t = (ptr: number, value: number) => void; +type Options_set_rtol_t = (ptr: number, value: number) => void; +type Options_set_linear_solver_t = (ptr: number, value: number) => void; +type Options_set_preconditioner_t = (ptr: number, value: number) => void; +type Options_set_jacobian_t = (ptr: number, value: number) => void; +type Options_set_linsol_max_iterations_t = (ptr: number, value: number) => void; +type Options_set_debug_t = (ptr: number, value: number) => void; +type Options_set_mxsteps_t = (ptr: number, value: number) => void; +type Options_set_min_step_t = (ptr: number, value: number) => void; +type Options_set_max_step_t = (ptr: number, value: number) => void; +type Options_get_linear_solver_t = (ptr: number) => number; type Options_get_fixed_times_t = (ptr: number) => number; -let Options_get_fixed_times: Options_get_fixed_times_t | undefined = undefined; type Options_get_print_stats_t = (ptr: number) => number; -let Options_get_print_stats: Options_get_print_stats_t | undefined = undefined; type Options_get_fwd_sens_t = (ptr: number) => number; -let Options_get_fwd_sens: Options_get_fwd_sens_t | undefined = undefined; -type Options_set_linear_solver_t = (ptr: number, linear_solver: number) => void; -let Options_set_linear_solver: Options_set_linear_solver_t | undefined = undefined; -type Options_get_linear_solver_t = (ptr: number) => number; -let Options_get_linear_solver: Options_get_linear_solver_t | undefined = undefined; -type Options_set_preconditioner_t = (ptr: number, preconditioner: number) => void; -let Options_set_preconditioner: Options_set_preconditioner_t | undefined = undefined; -type Options_get_preconditioner_t = (ptr: number) => number; -let Options_get_preconditioner: Options_get_preconditioner_t | undefined = undefined; -type Options_set_jacobian_t = (ptr: number, jacobian: number) => void; -let Options_set_jacobian: Options_set_jacobian_t | undefined = undefined; -type Options_get_jacobian_t = (ptr: number) => number; -let Options_get_jacobian: Options_get_jacobian_t | undefined = undefined; -type Options_set_atol_t = (ptr: number, atol: number) => void; -let Options_set_atol: Options_set_atol_t | undefined = undefined; type Options_get_atol_t = (ptr: number) => number; -let Options_get_atol: Options_get_atol_t | undefined = undefined; -type Options_set_rtol_t = (ptr: number, rtol: number) => void; -let Options_set_rtol: Options_set_rtol_t | undefined = undefined; type Options_get_rtol_t = (ptr: number) => number; -let Options_get_rtol: Options_get_rtol_t | undefined = undefined; -type Options_set_linsol_max_iterations_t = (ptr: number, linsol_max_iterations: number) => void; -let Options_set_linsol_max_iterations: Options_set_linsol_max_iterations_t | undefined = undefined; -type Options_get_linsol_max_iterations_t = (ptr: number) => number; -let Options_get_linsol_max_iterations: Options_get_linsol_max_iterations_t | undefined = undefined; -type Options_set_debug_t = (ptr: number, debug: number) => void; -let Options_set_debug: Options_set_debug_t | undefined = undefined; type Options_get_debug_t = (ptr: number) => number; -let Options_get_debug: Options_get_debug_t | undefined = undefined; -type Options_set_mxsteps_t = (ptr: number, mxsteps: number) => void; -let Options_set_mxsteps: Options_set_mxsteps_t | undefined = undefined; type Options_get_mxsteps_t = (ptr: number) => number; -let Options_get_mxsteps: Options_get_mxsteps_t | undefined = undefined; -type Options_set_min_step_t = (ptr: number, min_step: number) => void; -let Options_set_min_step: Options_set_min_step_t | undefined = undefined; type Options_get_min_step_t = (ptr: number) => number; -let Options_get_min_step: Options_get_min_step_t | undefined = undefined; -type Options_set_max_step_t = (ptr: number, max_step: number) => void; -let Options_set_max_step: Options_set_max_step_t | undefined = undefined; type Options_get_max_step_t = (ptr: number) => number; -let Options_get_max_step: Options_get_max_step_t | undefined = undefined; - - -export function extract_options_functions(obj: WebAssembly.WebAssemblyInstantiatedSource) { - Options_create = obj.instance.exports.Options_create as Options_create_t; - Options_destroy = obj.instance.exports.Options_destroy as Options_destroy_t; - Options_set_fixed_times = obj.instance.exports.Options_set_fixed_times as Options_set_fixed_times_t; - Options_set_print_stats = obj.instance.exports.Options_set_print_stats as Options_set_print_stats_t; - Options_get_fixed_times = obj.instance.exports.Options_get_fixed_times as Options_get_fixed_times_t; - Options_get_print_stats = obj.instance.exports.Options_get_print_stats as Options_get_print_stats_t; - Options_set_fwd_sens = obj.instance.exports.Options_set_fwd_sens as Options_set_fwd_sens_t; - Options_get_fwd_sens = obj.instance.exports.Options_get_fwd_sens as Options_get_fwd_sens_t; - Options_set_linear_solver = obj.instance.exports.Options_set_linear_solver as (ptr: number, linear_solver: number) => void; - Options_get_linear_solver = obj.instance.exports.Options_get_linear_solver as (ptr: number) => number; - Options_set_preconditioner = obj.instance.exports.Options_set_preconditioner as (ptr: number, preconditioner: number) => void; - Options_get_preconditioner = obj.instance.exports.Options_get_preconditioner as (ptr: number) => number; - Options_set_jacobian = obj.instance.exports.Options_set_jacobian as (ptr: number, jacobian: number) => void; - Options_get_jacobian = obj.instance.exports.Options_get_jacobian as (ptr: number) => number; - Options_set_atol = obj.instance.exports.Options_set_atol as (ptr: number, atol: number) => void; - Options_get_atol = obj.instance.exports.Options_get_atol as (ptr: number) => number; - Options_set_rtol = obj.instance.exports.Options_set_rtol as (ptr: number, rtol: number) => void; - Options_get_rtol = obj.instance.exports.Options_get_rtol as (ptr: number) => number; - Options_set_linsol_max_iterations = obj.instance.exports.Options_set_linsol_max_iterations as (ptr: number, linsol_max_iterations: number) => void; - Options_get_linsol_max_iterations = obj.instance.exports.Options_get_linsol_max_iterations as (ptr: number) => number; - Options_set_debug = obj.instance.exports.Options_set_debug as (ptr: number, debug: number) => void; - Options_get_debug = obj.instance.exports.Options_get_debug as (ptr: number) => number; - Options_set_mxsteps = obj.instance.exports.Options_set_mxsteps as (ptr: number, mxsteps: number) => void; - Options_get_mxsteps = obj.instance.exports.Options_get_mxsteps as (ptr: number) => number; - Options_set_min_step = obj.instance.exports.Options_set_min_step as (ptr: number, min_step: number) => void; - Options_get_min_step = obj.instance.exports.Options_get_min_step as (ptr: number) => number; - Options_set_max_step = obj.instance.exports.Options_set_max_step as (ptr: number, max_step: number) => void; - Options_get_max_step = obj.instance.exports.Options_get_max_step as (ptr: number) => number; +type Options_get_preconditioner_t = (ptr: number) => number; +type Options_get_jacobian_t = (ptr: number) => number; +type Options_get_linsol_max_iterations_t = (ptr: number) => number; + +export interface OptionsFunctions { + Options_create: Options_create_t; + Options_destroy: Options_destroy_t; + Options_set_fixed_times: Options_set_fixed_times_t; + Options_get_fixed_times: Options_get_fixed_times_t; + Options_set_print_stats: Options_set_print_stats_t; + Options_get_print_stats: Options_get_print_stats_t; + Options_set_fwd_sens: Options_set_fwd_sens_t; + Options_get_fwd_sens: Options_get_fwd_sens_t; + Options_set_atol: Options_set_atol_t; + Options_get_atol: Options_get_atol_t; + Options_set_rtol: Options_set_rtol_t; + Options_get_rtol: Options_get_rtol_t; + Options_set_linear_solver: Options_set_linear_solver_t; + Options_set_preconditioner: Options_set_preconditioner_t; + Options_set_jacobian: Options_set_jacobian_t; + Options_set_linsol_max_iterations: Options_set_linsol_max_iterations_t; + Options_set_debug: Options_set_debug_t; + Options_get_debug: Options_get_debug_t; + Options_set_mxsteps: Options_set_mxsteps_t; + Options_get_mxsteps: Options_get_mxsteps_t; + Options_set_min_step: Options_set_min_step_t; + Options_get_min_step: Options_get_min_step_t; + Options_set_max_step: Options_set_max_step_t; + Options_get_max_step: Options_get_max_step_t; + Options_get_linear_solver: Options_get_linear_solver_t; + Options_get_preconditioner: Options_get_preconditioner_t; + Options_get_jacobian: Options_get_jacobian_t; + Options_get_linsol_max_iterations: Options_get_linsol_max_iterations_t; +} + +export function extract_options_functions(obj: WebAssembly.WebAssemblyInstantiatedSource): OptionsFunctions { + return { + Options_create: obj.instance.exports.Options_create as Options_create_t, + Options_destroy: obj.instance.exports.Options_destroy as Options_destroy_t, + Options_set_fixed_times: obj.instance.exports.Options_set_fixed_times as Options_set_fixed_times_t, + Options_get_fixed_times: obj.instance.exports.Options_get_fixed_times as Options_get_fixed_times_t, + Options_set_print_stats: obj.instance.exports.Options_set_print_stats as Options_set_print_stats_t, + Options_get_print_stats: obj.instance.exports.Options_get_print_stats as Options_get_print_stats_t, + Options_set_fwd_sens: obj.instance.exports.Options_set_fwd_sens as Options_set_fwd_sens_t, + Options_get_fwd_sens: obj.instance.exports.Options_get_fwd_sens as Options_get_fwd_sens_t, + Options_set_atol: obj.instance.exports.Options_set_atol as Options_set_atol_t, + Options_get_atol: obj.instance.exports.Options_get_atol as Options_get_atol_t, + Options_set_rtol: obj.instance.exports.Options_set_rtol as Options_set_rtol_t, + Options_get_rtol: obj.instance.exports.Options_get_rtol as Options_get_rtol_t, + Options_set_linear_solver: obj.instance.exports.Options_set_linear_solver as Options_set_linear_solver_t, + Options_set_preconditioner: obj.instance.exports.Options_set_preconditioner as Options_set_preconditioner_t, + Options_set_jacobian: obj.instance.exports.Options_set_jacobian as Options_set_jacobian_t, + Options_set_linsol_max_iterations: obj.instance.exports.Options_set_linsol_max_iterations as Options_set_linsol_max_iterations_t, + Options_set_debug: obj.instance.exports.Options_set_debug as Options_set_debug_t, + Options_get_debug: obj.instance.exports.Options_get_debug as Options_get_debug_t, + Options_set_mxsteps: obj.instance.exports.Options_set_mxsteps as Options_set_mxsteps_t, + Options_get_mxsteps: obj.instance.exports.Options_get_mxsteps as Options_get_mxsteps_t, + Options_set_min_step: obj.instance.exports.Options_set_min_step as Options_set_min_step_t, + Options_get_min_step: obj.instance.exports.Options_get_min_step as Options_get_min_step_t, + Options_set_max_step: obj.instance.exports.Options_set_max_step as Options_set_max_step_t, + Options_get_max_step: obj.instance.exports.Options_get_max_step as Options_get_max_step_t, + Options_get_linear_solver: obj.instance.exports.Options_get_linear_solver as Options_get_linear_solver_t, + Options_get_preconditioner: obj.instance.exports.Options_get_preconditioner as Options_get_preconditioner_t, + Options_get_jacobian: obj.instance.exports.Options_get_jacobian as Options_get_jacobian_t, + Options_get_linsol_max_iterations: obj.instance.exports.Options_get_linsol_max_iterations as Options_get_linsol_max_iterations_t + }; +} + +export enum OptionsJacobian { + DENSE_JACOBIAN = 0, + SPARSE_JACOBIAN = 1, + NUMERICAL_JACOBIAN = 2, +} + +export enum OptionsPreconditioner { + PRECON_NONE = 0, + PRECON_LEFT = 1, + PRECON_RIGHT = 2, + PRECON_BOTH = 3, } export enum OptionsLinearSolver { @@ -98,22 +123,29 @@ export enum OptionsLinearSolver { LINEAR_SOLVER_SPTFQMR = 5, } -export enum OptionsPreconditioner { - PRECON_NONE = 0, - PRECON_LEFT = 1, - PRECON_RIGHT = 2, -} - -export enum OptionsJacobian { - DENSE_JACOBIAN = 0, - SPARSE_JACOBIAN = 1, - MATRIX_FREE_JACOBIAN = 2, - NO_JACOBIAN = 3, +class SimpleOpenFile { + file: File; + file_pos: number; + constructor(file: File) { + this.file = file; + this.file_pos = 0; + } + readToString() { + const data = this.file.data as Uint8Array + const start = this.file_pos; + const end = data.byteLength; + const slice = data.slice(start, end); + this.file_pos = end; + return new TextDecoder().decode(slice); + } } - class Options { pointer: number; + public functions: OptionsFunctions; + public stderr: SimpleOpenFile; + public stdout: SimpleOpenFile; + constructor({ mxsteps = 500, min_step = 0.0, @@ -128,64 +160,85 @@ class Options { jacobian = OptionsJacobian.DENSE_JACOBIAN, linsol_max_iterations = 100, debug = false - }) { - this.pointer = check_function(Options_create)(); - check_function(Options_set_fixed_times)(this.pointer, fixed_times ? 1 : 0); - check_function(Options_set_print_stats)(this.pointer, print_stats ? 1 : 0); - check_function(Options_set_fwd_sens)(this.pointer, fwd_sens ? 1 : 0); - check_function(Options_set_atol)(this.pointer, atol); - check_function(Options_set_rtol)(this.pointer, rtol); - check_function(Options_set_linear_solver)(this.pointer, linear_solver); - check_function(Options_set_preconditioner)(this.pointer, preconditioner); - check_function(Options_set_jacobian)(this.pointer, jacobian); - check_function(Options_set_linsol_max_iterations)(this.pointer, linsol_max_iterations); - check_function(Options_set_debug)(this.pointer, debug ? 1 : 0); - check_function(Options_set_mxsteps)(this.pointer, mxsteps); - check_function(Options_set_min_step)(this.pointer, min_step); - check_function(Options_set_max_step)(this.pointer, max_step); + }, modelId?: string) { + const model = getModel(modelId); + if (!model) { + throw new Error(`Model ${modelId || 'default'} not found. Please compile the model first.`); + } + this.functions = model.optionsFunctions; + this.stderr = model.stderr; + this.stdout = model.stdout; + this.pointer = check_function(this.functions.Options_create)(); + check_function(this.functions.Options_set_fixed_times)(this.pointer, fixed_times ? 1 : 0); + check_function(this.functions.Options_set_print_stats)(this.pointer, print_stats ? 1 : 0); + check_function(this.functions.Options_set_fwd_sens)(this.pointer, fwd_sens ? 1 : 0); + check_function(this.functions.Options_set_atol)(this.pointer, atol); + check_function(this.functions.Options_set_rtol)(this.pointer, rtol); + check_function(this.functions.Options_set_linear_solver)(this.pointer, linear_solver); + check_function(this.functions.Options_set_preconditioner)(this.pointer, preconditioner); + check_function(this.functions.Options_set_jacobian)(this.pointer, jacobian); + check_function(this.functions.Options_set_linsol_max_iterations)(this.pointer, linsol_max_iterations); + check_function(this.functions.Options_set_debug)(this.pointer, debug ? 1 : 0); + check_function(this.functions.Options_set_mxsteps)(this.pointer, mxsteps); + check_function(this.functions.Options_set_min_step)(this.pointer, min_step); + check_function(this.functions.Options_set_max_step)(this.pointer, max_step); } + destroy() { - check_function(Options_destroy)(this.pointer); + check_function(this.functions.Options_destroy)(this.pointer); } + + get_linear_solver() { + return check_function(this.functions.Options_get_linear_solver)(this.pointer); + } + get_fixed_times() { - return check_function(Options_get_fixed_times)(this.pointer) === 1; + return check_function(this.functions.Options_get_fixed_times)(this.pointer) === 1; } + get_print_stats() { - return check_function(Options_get_print_stats)(this.pointer) === 1; + return check_function(this.functions.Options_get_print_stats)(this.pointer) === 1; } + get_fwd_sens() { - return check_function(Options_get_fwd_sens)(this.pointer) === 1; + return check_function(this.functions.Options_get_fwd_sens)(this.pointer) === 1; } + get_atol() { - return check_function(Options_get_atol)(this.pointer); + return check_function(this.functions.Options_get_atol)(this.pointer); } + get_rtol() { - return check_function(Options_get_rtol)(this.pointer); - } - get_linear_solver() { - return check_function(Options_get_linear_solver)(this.pointer); - } - get_preconditioner() { - return check_function(Options_get_preconditioner)(this.pointer); - } - get_jacobian() { - return check_function(Options_get_jacobian)(this.pointer); - } - get_linsol_max_iterations() { - return check_function(Options_get_linsol_max_iterations)(this.pointer); + return check_function(this.functions.Options_get_rtol)(this.pointer); } + get_debug() { - return check_function(Options_get_debug)(this.pointer) === 1; + return check_function(this.functions.Options_get_debug)(this.pointer) === 1; } + get_mxsteps() { - return check_function(Options_get_mxsteps)(this.pointer); + return check_function(this.functions.Options_get_mxsteps)(this.pointer); } + get_min_step() { - return check_function(Options_get_min_step)(this.pointer); + return check_function(this.functions.Options_get_min_step)(this.pointer); } + get_max_step() { - return check_function(Options_get_max_step)(this.pointer); + return check_function(this.functions.Options_get_max_step)(this.pointer); + } + + get_preconditioner() { + return check_function(this.functions.Options_get_preconditioner)(this.pointer); + } + + get_jacobian() { + return check_function(this.functions.Options_get_jacobian)(this.pointer); + } + + get_linsol_max_iterations() { + return check_function(this.functions.Options_get_linsol_max_iterations)(this.pointer); } } - + export default Options; \ No newline at end of file diff --git a/src/solver.ts b/src/solver.ts index fc264d7..c1ce2a8 100644 --- a/src/solver.ts +++ b/src/solver.ts @@ -1,32 +1,41 @@ -import { stderr } from "./index"; import Options from "./options"; import { check_function } from "./utils"; -import Vector from "./vector"; +import Vector, { VectorFunctions } from "./vector"; +import { getModel } from "./index"; + +declare global { + var solverFunctions: SolverFunctions; + var vectorFunctions: VectorFunctions; +} type Solver_create_t = () => number; -let Solver_create: Solver_create_t | undefined = undefined; type Solver_destroy_t = (ptr: number) => void; -let Solver_destroy: Solver_destroy_t | undefined = undefined; type Solver_solve_t = (ptr: number, times: number, inputs: number, dinputs: number, outputs: number, doutputs: number) => number; -let Solver_solve: Solver_solve_t | undefined = undefined; type Solver_init_t = (ptr: number, options: number) => void; -let Solver_init: Solver_init_t | undefined = undefined; type Solver_number_of_states_t = (ptr: number) => number; -let Solver_number_of_states: Solver_number_of_states_t | undefined = undefined; type Solver_number_of_inputs_t = (ptr: number) => number; -let Solver_number_of_inputs: Solver_number_of_inputs_t | undefined = undefined; type Solver_number_of_outputs_t = (ptr: number) => number; -let Solver_number_of_outputs: Solver_number_of_outputs_t | undefined = undefined; +export interface SolverFunctions { + Solver_create: Solver_create_t; + Solver_destroy: Solver_destroy_t; + Solver_solve: Solver_solve_t; + Solver_init: Solver_init_t; + Solver_number_of_states: Solver_number_of_states_t; + Solver_number_of_inputs: Solver_number_of_inputs_t; + Solver_number_of_outputs: Solver_number_of_outputs_t; +} -export function extract_solver_functions(obj: WebAssembly.WebAssemblyInstantiatedSource) { - Solver_create = obj.instance.exports.Sundials_create as Solver_create_t; - Solver_destroy = obj.instance.exports.Sundials_destroy as Solver_destroy_t; - Solver_solve = obj.instance.exports.Sundials_solve as Solver_solve_t; - Solver_init = obj.instance.exports.Sundials_init as Solver_init_t; - Solver_number_of_states = obj.instance.exports.Sundials_number_of_states as Solver_number_of_states_t; - Solver_number_of_inputs = obj.instance.exports.Sundials_number_of_inputs as Solver_number_of_inputs_t; - Solver_number_of_outputs = obj.instance.exports.Sundials_number_of_outputs as Solver_number_of_outputs_t; +export function extract_solver_functions(obj: WebAssembly.WebAssemblyInstantiatedSource): SolverFunctions { + return { + Solver_create: obj.instance.exports.Sundials_create as Solver_create_t, + Solver_destroy: obj.instance.exports.Sundials_destroy as Solver_destroy_t, + Solver_solve: obj.instance.exports.Sundials_solve as Solver_solve_t, + Solver_init: obj.instance.exports.Sundials_init as Solver_init_t, + Solver_number_of_states: obj.instance.exports.Sundials_number_of_states as Solver_number_of_states_t, + Solver_number_of_inputs: obj.instance.exports.Sundials_number_of_inputs as Solver_number_of_inputs_t, + Solver_number_of_outputs: obj.instance.exports.Sundials_number_of_outputs as Solver_number_of_outputs_t + }; } class Solver { @@ -36,33 +45,45 @@ class Solver { number_of_states: number; options: Options; dummy_vector: Vector; - constructor(options: Options) { + private functions: SolverFunctions; + private vectorFunctions: VectorFunctions; + + constructor(options: Options, modelId?: string) { + const model = getModel(modelId); + if (!model) { + throw new Error(`Model ${modelId || 'default'} not found. Please compile the model first.`); + } + this.functions = model.solverFunctions; + this.vectorFunctions = model.vectorFunctions; this.options = options; - this.pointer = check_function(Solver_create)(); - check_function(Solver_init)(this.pointer, options.pointer); - this.number_of_inputs = check_function(Solver_number_of_inputs)(this.pointer); - this.number_of_outputs = check_function(Solver_number_of_outputs)(this.pointer); - this.number_of_states = check_function(Solver_number_of_states)(this.pointer); - this.dummy_vector = new Vector([]); + this.pointer = check_function(this.functions.Solver_create)(); + check_function(this.functions.Solver_init)(this.pointer, options.pointer); + this.number_of_inputs = check_function(this.functions.Solver_number_of_inputs)(this.pointer); + this.number_of_outputs = check_function(this.functions.Solver_number_of_outputs)(this.pointer); + this.number_of_states = check_function(this.functions.Solver_number_of_states)(this.pointer); + this.dummy_vector = new Vector([], this.vectorFunctions); } + destroy() { - check_function(Solver_destroy)(this.pointer); + check_function(this.functions.Solver_destroy)(this.pointer); } + solve(times: Vector, inputs: Vector, outputs: Vector) { if (inputs.length() != this.number_of_inputs) { - throw new Error(`Expected ${this.number_of_inputs} inputs, got ${inputs.length}`); + throw new Error(`Expected ${this.number_of_inputs} inputs, got ${inputs.length()}`); } if (times.length() < 2) { throw new Error("Times vector must have at least two elements"); } - const result = check_function(Solver_solve)(this.pointer, times.pointer, inputs.pointer, this.dummy_vector.pointer, outputs.pointer, this.dummy_vector.pointer); + const result = check_function(this.functions.Solver_solve)(this.pointer, times.pointer, inputs.pointer, this.dummy_vector.pointer, outputs.pointer, this.dummy_vector.pointer); if (result != 0) { - throw new Error(stderr.readToString()); + throw new Error(this.options.stderr.readToString()); } } + solve_with_sensitivities(times: Vector, inputs: Vector, dinputs: Vector, outputs: Vector, doutputs: Vector) { if (inputs.length() != this.number_of_inputs) { - throw new Error(`Expected ${this.number_of_inputs} inputs, got ${inputs.length}`); + throw new Error(`Expected ${this.number_of_inputs} inputs, got ${inputs.length()}`); } if (inputs.length() != dinputs.length()) { throw new Error(`Expected ${inputs.length()} dinputs, got ${dinputs.length()}`); @@ -71,9 +92,9 @@ class Solver { throw new Error("Times vector must have at least two elements"); } - const result = check_function(Solver_solve)(this.pointer, times.pointer, inputs.pointer, dinputs.pointer, outputs.pointer, doutputs.pointer); + const result = check_function(this.functions.Solver_solve)(this.pointer, times.pointer, inputs.pointer, dinputs.pointer, outputs.pointer, doutputs.pointer); if (result != 0) { - throw new Error(stderr.readToString()); + throw new Error(this.options.stderr.readToString()); } } } diff --git a/src/vector.ts b/src/vector.ts index d1a3840..1109ba8 100644 --- a/src/vector.ts +++ b/src/vector.ts @@ -1,66 +1,86 @@ import { check_function } from "./utils"; -import { getWasmMemory } from "./index"; + +declare global { + var vectorFunctions: VectorFunctions; +} type Vector_create_t = () => number; -let Vector_create: Vector_create_t | undefined = undefined; type Vector_destroy_t = (ptr: number) => void; -let Vector_destroy: Vector_destroy_t | undefined = undefined; type Vector_linspace_create_t = (start: number, stop: number, len: number) => number; -let Vector_linspace_create: Vector_linspace_create_t | undefined = undefined; type Vector_create_with_capacity_t = (len: number, capacity: number) => number; -let Vector_create_with_capacity: Vector_create_with_capacity_t | undefined = undefined; type Vector_push_t = (ptr: number, value: number) => void; -let Vector_push: Vector_push_t | undefined = undefined; type Vector_get_t = (ptr: number, index: number) => number; -let Vector_get: Vector_get_t | undefined = undefined; type Vector_set_t = (ptr: number, index: number, value: number) => void; -let Vector_set: Vector_set_t | undefined = undefined; type Vector_get_data_t = (ptr: number) => number; -let Vector_get_data: Vector_get_data_t | undefined = undefined; type Vector_get_length_t = (ptr: number) => number; -let Vector_get_length: Vector_get_length_t | undefined = undefined; type Vector_resize_t = (ptr: number, len: number) => void; -let Vector_resize: Vector_resize_t | undefined = undefined; +export interface VectorFunctions { + Vector_create: Vector_create_t; + Vector_destroy: Vector_destroy_t; + Vector_linspace_create: Vector_linspace_create_t; + Vector_create_with_capacity: Vector_create_with_capacity_t; + Vector_push: Vector_push_t; + Vector_get: Vector_get_t; + Vector_set: Vector_set_t; + Vector_get_data: Vector_get_data_t; + Vector_get_length: Vector_get_length_t; + Vector_resize: Vector_resize_t; + memory: WebAssembly.Memory; +} -export function extract_vector_functions(obj: WebAssembly.WebAssemblyInstantiatedSource) { - Vector_create = obj.instance.exports.Vector_create as Vector_create_t; - Vector_destroy = obj.instance.exports.Vector_destroy as Vector_destroy_t; - Vector_linspace_create = obj.instance.exports.Vector_linspace_create as Vector_linspace_create_t; - Vector_create_with_capacity = obj.instance.exports.Vector_create_with_capacity as Vector_create_with_capacity_t; - Vector_push = obj.instance.exports.Vector_push as Vector_push_t; - Vector_get = obj.instance.exports.Vector_get as Vector_get_t; - Vector_set = obj.instance.exports.Vector_set as Vector_set_t; - Vector_get_data = obj.instance.exports.Vector_get_data as Vector_get_data_t; - Vector_get_length = obj.instance.exports.Vector_get_length as Vector_get_length_t; - Vector_resize = obj.instance.exports.Vector_resize as Vector_resize_t; +export function extract_vector_functions(obj: WebAssembly.WebAssemblyInstantiatedSource): VectorFunctions { + return { + Vector_create: obj.instance.exports.Vector_create as Vector_create_t, + Vector_destroy: obj.instance.exports.Vector_destroy as Vector_destroy_t, + Vector_linspace_create: obj.instance.exports.Vector_linspace_create as Vector_linspace_create_t, + Vector_create_with_capacity: obj.instance.exports.Vector_create_with_capacity as Vector_create_with_capacity_t, + Vector_push: obj.instance.exports.Vector_push as Vector_push_t, + Vector_get: obj.instance.exports.Vector_get as Vector_get_t, + Vector_set: obj.instance.exports.Vector_set as Vector_set_t, + Vector_get_data: obj.instance.exports.Vector_get_data as Vector_get_data_t, + Vector_get_length: obj.instance.exports.Vector_get_length as Vector_get_length_t, + Vector_resize: obj.instance.exports.Vector_resize as Vector_resize_t, + memory: obj.instance.exports.memory as WebAssembly.Memory + }; } class Vector { pointer: number; - constructor(array: number[]) { - this.pointer = check_function(Vector_create_with_capacity)(0, array.length) - let push = check_function(Vector_push); + private functions: VectorFunctions; + + constructor(array: number[], functions?: VectorFunctions) { + this.functions = functions || global.vectorFunctions; + if (!this.functions) { + throw new Error('Vector functions not available. Please compile the model first.'); + } + this.pointer = check_function(this.functions.Vector_create_with_capacity)(0, array.length) + let push = check_function(this.functions.Vector_push); for (let i = 0; i < array.length; i++) { push(this.pointer, array[i]); } } + get(index: number) { - return check_function(Vector_get)(this.pointer, index); + return check_function(this.functions.Vector_get)(this.pointer, index); } + getFloat64Array() { - const length = check_function(Vector_get_length)(this.pointer); - const data = check_function(Vector_get_data)(this.pointer); - return new Float64Array(getWasmMemory().buffer, data, length); + const length = check_function(this.functions.Vector_get_length)(this.pointer); + const data = check_function(this.functions.Vector_get_data)(this.pointer); + return new Float64Array(this.functions.memory.buffer, data, length); } + destroy() { - check_function(Vector_destroy)(this.pointer); + check_function(this.functions.Vector_destroy)(this.pointer); } + resize(len: number) { - check_function(Vector_resize)(this.pointer, len); + check_function(this.functions.Vector_resize)(this.pointer, len); } + length() { - return check_function(Vector_get_length)(this.pointer); + return check_function(this.functions.Vector_get_length)(this.pointer); } } diff --git a/test/compile.spec.ts b/test/compile.spec.ts index bfbbfd1..da9169b 100644 --- a/test/compile.spec.ts +++ b/test/compile.spec.ts @@ -1,12 +1,12 @@ -import { describe, it, before } from 'mocha'; +import { describe, it } from 'mocha'; import { assert } from 'chai'; -import { compileModel } from '../src/index'; -import logistic_code from './logistic'; +import { compileModel } from '/src'; +import { code1 } from './logistic'; describe('Solver', function () { it('can compile a good model', function () { - return compileModel(logistic_code).then(() => { + return compileModel(code1).then(() => { assert(true); }).catch((e) => { assert.fail(e); @@ -16,7 +16,7 @@ describe('Solver', function () { it('fails on bad model', function () { return compileModel("a { 1 }").then(() => { assert.fail("Should have failed"); - }).catch((e) => { + }).catch(() => { assert(true); }); }); diff --git a/test/logistic.ts b/test/logistic.ts index 9ba2dff..bc274b5 100644 --- a/test/logistic.ts +++ b/test/logistic.ts @@ -1,4 +1,4 @@ -const code = ` +const code1 = ` in = [r, k] r { 1 } k { 1 } @@ -23,4 +23,29 @@ out_i { z, }`; -export default code; \ No newline at end of file +const code2 = ` +in = [r, k] +r { 1 } +k { 1 } +u_i { + y = 1, + z = 0, +} +dudt_i { + dydt = 0, + dzdt = 0, +} +F_i { + dydt, + 0, +} +G_i { + (r * y) * (1 - (y / k)), + (y * z) - (k * z), +} +out_i { + y, + z, +}`; + +export { code1, code2 }; \ No newline at end of file diff --git a/test/options.spec.ts b/test/options.spec.ts index 4ae36c3..bbfe274 100644 --- a/test/options.spec.ts +++ b/test/options.spec.ts @@ -1,14 +1,13 @@ import Options, { OptionsJacobian, OptionsLinearSolver, OptionsPreconditioner } from '../src/options'; import { describe, it, before } from 'mocha'; import { assert } from 'chai'; -import { compileModel, compileResponse } from '../src/index'; -import * as fs from 'fs'; -import logistic_code from './logistic'; +import { compileModel } from '/src'; +import { code1 } from './logistic'; describe('Options', function () { before(function () { - return compileModel(logistic_code); + return compileModel(code1); }); it('can construct and destroy', function () { diff --git a/test/solver.spec.ts b/test/solver.spec.ts index 38dc4f3..a380bf1 100644 --- a/test/solver.spec.ts +++ b/test/solver.spec.ts @@ -3,15 +3,13 @@ import Solver from '../src/solver'; import Options from '../src/options'; import { describe, it, before } from 'mocha'; import { assert, expect } from 'chai'; -import * as fs from 'fs'; -import { compileModel } from '../src/index'; -import logistic_code from './logistic'; -import { error } from 'console'; +import { compileModel } from '/src'; +import { code1 } from './logistic'; -describe('Solver', function () { +describe('Single Solver', function () { before(function () { - return compileModel(logistic_code); + return compileModel(code1); }); it('can construct and destroy', function () { @@ -143,4 +141,4 @@ describe('Solver', function () { solver.destroy(); }); -}); +}); \ No newline at end of file diff --git a/test/solver2.spec.ts b/test/solver2.spec.ts new file mode 100644 index 0000000..1b13082 --- /dev/null +++ b/test/solver2.spec.ts @@ -0,0 +1,129 @@ +import Vector from '../src/vector'; +import Solver from '../src/solver'; +import Options from '../src/options'; +import { describe, it } from 'mocha'; +import { assert } from 'chai'; +import { compileModel, getModel } from '/src'; +import { code1, code2 } from './logistic'; + +describe('Multiple Solvers', function () { + it('can compile code1 and code2', async function () { + try { + await Promise.all([ + compileModel(code1, 'model1'), + compileModel(code2, 'model2') + ]); + assert(true); + } catch (e) { + assert.fail(e as string); + } + }); + + + it('can compile and solve using global functions', async function () { + await compileModel(code1); + let options = new Options({}); + let solver = new Solver(options); + + let times = new Vector([0, 2]); + let inputs = new Vector([1, 2]); + let outputs = new Vector(new Array(times.length() * solver.number_of_outputs)); + + solver.solve(times, inputs, outputs); + + const outputArray = outputs.getFloat64Array(); + const sum = outputArray.reduce((acc, curr) => acc + curr, 0); + const expectedSum = 167.28648630426795; + + assert.approximately(sum, expectedSum, 0.0001); + + solver.destroy(); + }); + + it('can compile and solve using default model', async function () { + await compileModel(code1); + let options = new Options({}); + let solver = new Solver(options); + + let times = new Vector([0, 2], getModel().vectorFunctions); + let inputs = new Vector([1, 2], getModel().vectorFunctions); + let outputs = new Vector(new Array(times.length() * solver.number_of_outputs), getModel().vectorFunctions); + + solver.solve(times, inputs, outputs); + + const outputArray = outputs.getFloat64Array(); + const sum = outputArray.reduce((acc, curr) => acc + curr, 0); + const expectedSum = 167.28648630426795; + + assert.approximately(sum, expectedSum, 0.0001); + + solver.destroy(); + }); + + it('can compile using id and solve', async function () { + await compileModel(code1, 'model1'); + + // Solve code1 (default model) + let options1 = new Options({}, 'model1'); + let solver1 = new Solver(options1, 'model1'); + let times1 = new Vector([0, 2], getModel('model1').vectorFunctions); + let inputs1 = new Vector([1, 2], getModel('model1').vectorFunctions); + let outputs1 = new Vector(new Array(times1.length() * solver1.number_of_outputs), getModel('model1').vectorFunctions); + + solver1.solve(times1, inputs1, outputs1); + + const outputArray1 = outputs1.getFloat64Array(); + const sum1 = outputArray1.reduce((acc, curr) => acc + curr, 0); + const expectedSum1 = 167.28648630426795; + + assert.approximately(sum1, expectedSum1, 0.0001); + + solver1.destroy(); + }); + + it('can compile and solve both code1 and code2, and then solve code1 again', async function () { + await compileModel(code1, 'model1'); + await compileModel(code2, 'model2'); + + // Solve code1 (default model) + let options1 = new Options({}, 'model1'); + let solver1 = new Solver(options1, 'model1'); + let times1 = new Vector([0, 2], getModel('model1').vectorFunctions); + let inputs1 = new Vector([1, 2], getModel('model1').vectorFunctions); + let outputs1 = new Vector(new Array(times1.length() * solver1.number_of_outputs), getModel('model1').vectorFunctions); + + solver1.solve(times1, inputs1, outputs1); + const outputArray1 = outputs1.getFloat64Array(); + const sum1 = outputArray1.reduce((acc, curr) => acc + curr, 0); + const expectedSum1 = 167.28648630426795; + assert.approximately(sum1, expectedSum1, 0.0001); + + + // Solve code2 (model2) + let options2 = new Options({}, 'model2'); + let solver2 = new Solver(options2, 'model2'); + let times2 = new Vector([0, 2], getModel('model2').vectorFunctions); + let inputs2 = new Vector([1, 2], getModel('model2').vectorFunctions); + let outputs2 = new Vector(new Array(times2.length() * solver2.number_of_outputs), getModel('model2').vectorFunctions); + + solver2.solve(times2, inputs2, outputs2); + const outputArray2 = outputs2.getFloat64Array(); + const sum2 = outputArray2.reduce((acc, curr) => acc + curr, 0); + const expectedSum2 = 45.98250398335723; + assert.approximately(sum2, expectedSum2, 0.0001); + + + //solve code1 again + let times3 = new Vector([0, 2], getModel('model1').vectorFunctions); + let inputs3 = new Vector([1, 3], getModel('model1').vectorFunctions); + let outputs3 = new Vector(new Array(times3.length() * solver1.number_of_outputs), getModel('model1').vectorFunctions); + + + solver1.solve(times3, inputs3, outputs3); + const outputArray3 = outputs3.getFloat64Array(); + const sum3 = outputArray3.reduce((acc, curr) => acc + curr, 0); + const expectedSum3 = 176.28342332633014; + + assert.approximately(sum3, expectedSum3, 0.0001); + }); +}); \ No newline at end of file diff --git a/test/vector.spec.ts b/test/vector.spec.ts index 9dcf2a5..026dce2 100644 --- a/test/vector.spec.ts +++ b/test/vector.spec.ts @@ -1,14 +1,13 @@ import Vector from '../src/vector'; import { describe, it, before } from 'mocha'; import { assert } from 'chai'; -import * as fs from 'fs'; -import logistic_code from './logistic'; -import { compileModel } from '../src/index'; +import { code1 } from './logistic'; +import { compileModel } from '/src'; describe('Vector', function () { before(function () { - return compileModel(logistic_code); + return compileModel(code1); }); it('can construct and destroy', function () { diff --git a/tsconfig.json b/tsconfig.json index 6e4425e..21739c0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -10,7 +10,8 @@ }, "baseUrl": ".", "target": "es6", - "module": "es6", + "module": "esnext", + "moduleResolution": "node", "lib": ["es6", "dom", "ES2015", "ES2016", "ES2017", "ES2018", "ES2019", "ES2020", "ESNext"], "sourceMap": true, "strict": true, @@ -21,6 +22,5 @@ "compilerOptions": { "moduleDetection": "force" } - } } \ No newline at end of file diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..757866a --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,25 @@ +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' +import { resolve } from 'path' + +export default defineConfig({ + plugins: [vue()], + define: { + global: 'window' + }, + build: { + lib: { + entry: resolve(__dirname, 'src/index.ts'), + name: 'DiffEqJS', + fileName: (format) => `diffeq-js.${format}.js` + }, + rollupOptions: { + external: ['vue'], + output: { + globals: { + vue: 'Vue' + } + } + } + } +}) \ No newline at end of file