diff --git a/build/README.md b/build/README.md index 9f5653ea7..5e7de04d6 100644 --- a/build/README.md +++ b/build/README.md @@ -30,11 +30,15 @@ In [1]: from ctypes import CDLL, c_int, c_char_p, create_string_buffer, byref In [2]: c_dll = CDLL("libribasim", winmode=0x08) # winmode for Windows -In [3]: config_path = "ribasim.toml" +In [3]: argument = create_string_buffer(0) + ...: c_dll.jl_init_with_image_handle(c_int(0), byref(argument)) +Out[3]: 1 -In [4]: c_dll.initialize(c_char_p(config_path.encode())) -Out[4]: 0 +In [4]: config_path = "ribasim.toml" -In [5]: c_dll.update() +In [5]: c_dll.initialize(c_char_p(config_path.encode())) Out[5]: 0 + +In [6]: c_dll.update() +Out[6]: 0 ``` diff --git a/build/build.jl b/build/build.jl index 0d8d9662a..3ce4895ab 100644 --- a/build/build.jl +++ b/build/build.jl @@ -19,6 +19,7 @@ function (@main)(_)::Cint Pkg.activate(".") rm(output_dir; force = true, recursive = true) + cpu_target = default_app_cpu_target() image_recipe = ImageRecipe(; output_type = "--output-lib", @@ -26,6 +27,7 @@ function (@main)(_)::Cint project = project_dir, add_ccallables = true, verbose = true, + cpu_target, ) link_recipe = LinkRecipe(; image_recipe, outname = "build/ribasim/libribasim") bundle_recipe = BundleRecipe(; link_recipe, output_dir) @@ -146,3 +148,13 @@ function add_metadata(project_dir, license_file, output_dir, git_repo, readme) # Override the Cargo.toml file with the git version set_version("build/cli/Cargo.toml", tag) end + +# TODO make the default https://github.com/JuliaLang/JuliaC.jl/issues/33 +function default_app_cpu_target() + Sys.ARCH === :i686 ? "pentium4;sandybridge,-xsaveopt,clone_all" : + Sys.ARCH === :x86_64 ? + "generic;sandybridge,-xsaveopt,clone_all;haswell,-rdrnd,base(1)" : + Sys.ARCH === :arm ? "armv7-a;armv7-a,neon;armv7-a,neon,vfp4" : + Sys.ARCH === :aarch64 ? "generic" : #= is this really the best here? =# + Sys.ARCH === :powerpc64le ? "pwr8" : "generic" +end diff --git a/build/cli/src/main.rs b/build/cli/src/main.rs index 64a1f3854..2e3a25dad 100644 --- a/build/cli/src/main.rs +++ b/build/cli/src/main.rs @@ -54,14 +54,23 @@ fn main() -> ExitCode { } let shared_lib_path = match OS { - "windows" => exe_dir.join("bin/libribasim.dll"), - "linux" => exe_dir.join("lib/libribasim.so"), - "macos" => exe_dir.join("lib/libribasim.dylib"), + "windows" => "bin/libribasim.dll", + "linux" => "lib/libribasim.so", + "macos" => "lib/libribasim.dylib", _ => unimplemented!("Your OS is not supported yet."), }; + let full_shared_lib_path = exe_dir.join(shared_lib_path); unsafe { // Load the library - let lib = Library::new(shared_lib_path).unwrap(); + let lib = Library::new(full_shared_lib_path).unwrap(); + + // Init Julia + let jl_init_with_image_file: Symbol i32> = + lib.get(b"jl_init_with_image_file").unwrap(); + + let julia_bindir = CString::new(exe_dir.to_str().unwrap()).unwrap(); + let image_path = CString::new(shared_lib_path).unwrap(); + jl_init_with_image_file(julia_bindir.as_ptr(), image_path.as_ptr()); // Execute let execute: Symbol i32> = diff --git a/pixi.toml b/pixi.toml index dadeb061d..e70b335b3 100644 --- a/pixi.toml +++ b/pixi.toml @@ -75,7 +75,7 @@ lint = { depends-on = [ "mypy-ribasim-qgis", ] } # Build -build = { "cmd" = "julia --project --check-bounds=yes build/build.jl", depends-on = [ +build = { "cmd" = "julia --project --check-bounds=yes build/build.jl", env = { "JULIA_CPU_TARGET" = "generic;sandybridge,-xsaveopt,clone_all;haswell,-rdrnd,base(1)" }, depends-on = [ "generate-testmodels", "initialize-julia", ] } diff --git a/python/ribasim_api/ribasim_api/ribasim_api.py b/python/ribasim_api/ribasim_api/ribasim_api.py index 550ae7d57..b33766977 100644 --- a/python/ribasim_api/ribasim_api/ribasim_api.py +++ b/python/ribasim_api/ribasim_api/ribasim_api.py @@ -1,3 +1,6 @@ +# %% +from ctypes import byref, c_int, create_string_buffer + from xmipy import XmiWrapper @@ -18,6 +21,13 @@ def get_constant_int(self, name: str) -> int: return 1025 raise ValueError(f"{name} does not map to an integer exposed by Ribasim") + def jl_init_with_image_handle(self) -> None: + argument = create_string_buffer(0) + self.lib.jl_init_with_image_handle(c_int(0), byref(argument)) + + def shutdown_julia(self) -> None: + self.lib.shutdown_julia(c_int(0)) + def update_subgrid_level(self) -> None: self.lib.update_subgrid_level() diff --git a/python/ribasim_api/tests/conftest.py b/python/ribasim_api/tests/conftest.py index c9685c23a..4ad2b4444 100644 --- a/python/ribasim_api/tests/conftest.py +++ b/python/ribasim_api/tests/conftest.py @@ -6,6 +6,7 @@ from ribasim_api import RibasimApi from ribasim_testmodels import ( basic_model, + basic_transient_model, leaky_bucket_model, two_basin_model, user_demand_model, @@ -22,6 +23,13 @@ def libribasim_paths() -> tuple[Path, Path]: return lib_path, lib_folder +@pytest.fixture(scope="session", autouse=True) +def load_julia(libribasim_paths) -> None: + lib_path, lib_folder = libribasim_paths + libribasim = RibasimApi(lib_path, lib_folder) + libribasim.jl_init_with_image_handle() + + @pytest.fixture(scope="function") def libribasim(libribasim_paths, request) -> RibasimApi: lib_path, lib_folder = libribasim_paths @@ -38,6 +46,11 @@ def basic() -> ribasim.Model: return basic_model() +@pytest.fixture(scope="session") +def basic_transient(basic) -> ribasim.Model: + return basic_transient_model(basic) + + @pytest.fixture(scope="session") def leaky_bucket() -> ribasim.Model: return leaky_bucket_model()