Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,9 @@ if(MUSICA_ENABLE_TESTS)
setup_target_for_coverage_lcov(
NAME coverage
EXECUTABLE "ctest"
EXCLUDE "${PROJECT_SOURCE_DIR}/test/*"
BASE_DIRECTORY "${PROJECT_SOURCE_DIR}/src")
EXCLUDE "${PROJECT_SOURCE_DIR}/src/test/*"
BASE_DIRECTORY "${PROJECT_SOURCE_DIR}/src"
LCOV_ARGS "--ignore-errors" "mismatch")
Comment thread
K20shores marked this conversation as resolved.

endif()

Expand Down
2 changes: 1 addition & 1 deletion cmake/dependencies.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ endif()

if(MUSICA_BUILD_C_CXX_INTERFACE)
set_git_default(MECH_CONFIG_GIT_REPOSITORY https://github.com/NCAR/MechanismConfiguration.git)
set_git_default(MECH_CONFIG_GIT_TAG v1.0.0)
set_git_default(MECH_CONFIG_GIT_TAG 9bd4f9a36af83e3bd2b74185c9edb897555e8410)
Comment thread
K20shores marked this conversation as resolved.

FetchContent_Declare(mechanism_configuration
GIT_REPOSITORY ${MECH_CONFIG_GIT_REPOSITORY}
Expand Down
2 changes: 1 addition & 1 deletion docker/Dockerfile.coverage
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM fedora:37
FROM fedora:41
Comment thread
K20shores marked this conversation as resolved.

ARG MUSICA_GIT_TAG=main
ARG BUILD_TYPE=Release
Expand Down
7 changes: 7 additions & 0 deletions include/musica/micm/micm_c_interface.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ namespace musica
/// @return Pointer to MICM object
MICM *CreateMicmFromChemistryMechanism(const Chemistry *chemistry, MICMSolver solver_type, Error *error);

/// @brief Create a MICM object from a JSON or YAML configuration string
/// @param config_string JSON or YAML configuration string
/// @param solver_type Type of MICMSolver
/// @param error Error struct to indicate success or failure
/// @return Pointer to MICM object
Comment thread
K20shores marked this conversation as resolved.
MICM *CreateMicmFromConfigString(const char *config_string, MICMSolver solver_type, Error *error);

/// @brief Deletes a MICM object
/// @param micm Pointer to MICM object
/// @param error Error struct to indicate success or failure
Expand Down
1 change: 1 addition & 0 deletions include/musica/micm/parse.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ inline std::error_code make_error_code(MusicaParseErrc e)
namespace musica
{
Chemistry ReadConfiguration(const std::string& config_path);
Chemistry ReadConfigurationFromString(const std::string& json_or_yaml_string); // Parse from JSON/YAML string
Comment thread
K20shores marked this conversation as resolved.
Chemistry ParserV0(const mechanism_configuration::ParserResult<>& result);
Chemistry ConvertV1Mechanism(
const mechanism_configuration::v1::types::Mechanism& v1_mechanism,
Expand Down
3 changes: 2 additions & 1 deletion javascript/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@ string(REPLACE "\"" "" NODE_ADDON_API_DIR ${NODE_ADDON_API_DIR_RAW})
string(STRIP ${NODE_ADDON_API_DIR} NODE_ADDON_API_DIR)

# Create the addon library
add_library(${PROJECT_NAME} SHARED ${CMAKE_JS_SRC})
add_library(${PROJECT_NAME} SHARED)

target_sources(
${PROJECT_NAME}
PRIVATE
${CMAKE_JS_SRC}
src/musica_addon.cpp
src/micm/state_wrapper.cpp
src/micm/micm_wrapper.cpp
Expand Down
55 changes: 47 additions & 8 deletions javascript/micm/micm.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,57 @@ const { SolverType } = require('./solver');
const { SolverStats, SolverResult } = require('./solver_result');

class MICM {
constructor({ config_path = null, solver_type = null } = {}) {
if (solver_type === null) {
solver_type = SolverType.rosenbrock_standard_order;
/**
* Create a MICM solver instance from a configuration file path
*
* @param {string} configPath - Path to the mechanism configuration directory
* @param {number} [solverType=SolverType.rosenbrock_standard_order] - Type of solver to use
* @returns {MICM} A new MICM instance
*/
static fromConfigPath(configPath, solverType = SolverType.rosenbrock_standard_order) {
if (typeof configPath !== 'string') {
throw new TypeError('configPath must be a string');
}

if (config_path === null) {
throw new Error('config_path must be provided');
try {
const nativeMICM = addon.MICM.fromConfigPath(configPath, solverType);
return new MICM(nativeMICM, solverType);
} catch (error) {
throw new Error(`Failed to create MICM solver from config path: ${error.message}`);
Comment thread
K20shores marked this conversation as resolved.
}
}

/**
* Create a MICM solver instance from a Mechanism object
*
* @param {Object} mechanism - Mechanism object created in code
* @param {number} [solverType=SolverType.rosenbrock_standard_order] - Type of solver to use
* @returns {MICM} A new MICM instance
*/
static fromMechanism(mechanism, solverType = SolverType.rosenbrock_standard_order) {
if (!mechanism || typeof mechanism.getJSON !== 'function') {
throw new TypeError('mechanism must be a valid Mechanism object with getJSON() method');
}

try {
// JavaScript Mechanism → JSON String → C++ Parser
const mechanismJSON = mechanism.getJSON();
const jsonString = JSON.stringify(mechanismJSON);

const nativeMICM = addon.MICM.fromConfigString(jsonString, solverType);
return new MICM(nativeMICM, solverType);
} catch (error) {
throw new Error(`Failed to create MICM solver from mechanism: ${error.message}`);
Comment thread
K20shores marked this conversation as resolved.
}
}

// Create native MICM instance
this._nativeMICM = new addon.MICM(config_path, solver_type);
this._solverType = solver_type;
/**
* Private constructor - use static factory methods instead
* @private
*/
constructor(nativeMICM, solverType) {
this._nativeMICM = nativeMICM;
this._solverType = solverType;
}

solverType() {
Expand Down
84 changes: 74 additions & 10 deletions javascript/src/micm/micm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,37 +10,99 @@ using namespace musica_addon;

MICMClass::MICMClass(const Napi::CallbackInfo& info)
: Napi::ObjectWrap<MICMClass>(info)
{
// Constructor can be empty when using factory methods
// Factory methods will initialize micm_ after construction
return;
}

Napi::Value MICMClass::FromConfigPath(const Napi::CallbackInfo& info)
{
Napi::Env env = info.Env();

if (info.Length() < 2)
if (info.Length() < 1)
{
Napi::TypeError::New(env, "Expected 2 arguments: config_path and solver_type").ThrowAsJavaScriptException();
return;
Napi::TypeError::New(env, "Expected at least 1 argument: config_path").ThrowAsJavaScriptException();
return env.Null();
}

if (!info[0].IsString())
{
Napi::TypeError::New(env, "config_path must be a string").ThrowAsJavaScriptException();
return;
return env.Null();
}

if (!info[1].IsNumber())
std::string config_path = info[0].As<Napi::String>().Utf8Value();
int solver_type = 1; // Default to rosenbrock_standard_order

if (info.Length() >= 2 && info[1].IsNumber())
{
Napi::TypeError::New(env, "solver_type must be a number").ThrowAsJavaScriptException();
return;
solver_type = info[1].As<Napi::Number>().Int32Value();
}

std::string config_path = info[0].As<Napi::String>().Utf8Value();
int solver_type = info[1].As<Napi::Number>().Int32Value();
try
{
// Get the constructor function
Napi::Function cons = env.GetInstanceData<Napi::FunctionReference>()->Value();

// Create new instance
Napi::Object instance = cons.New({});
MICMClass* obj = Napi::ObjectWrap<MICMClass>::Unwrap(instance);

// Initialize the wrapper with config path using factory method
obj->micm_ = MICMWrapper::FromConfigPath(config_path, solver_type);

return instance;
}
catch (const std::exception& e)
{
Napi::Error::New(env, e.what()).ThrowAsJavaScriptException();
return env.Null();
}
}

Napi::Value MICMClass::FromConfigString(const Napi::CallbackInfo& info)
{
Napi::Env env = info.Env();

if (info.Length() < 1)
{
Napi::TypeError::New(env, "Expected at least 1 argument: config_string").ThrowAsJavaScriptException();
return env.Null();
}

if (!info[0].IsString())
{
Napi::TypeError::New(env, "config_string must be a string").ThrowAsJavaScriptException();
return env.Null();
}

std::string config_string = info[0].As<Napi::String>().Utf8Value();
int solver_type = 1; // Default to rosenbrock_standard_order

if (info.Length() >= 2 && info[1].IsNumber())
{
solver_type = info[1].As<Napi::Number>().Int32Value();
}

try
{
micm_ = std::make_unique<MICMWrapper>(config_path.c_str(), static_cast<musica::MICMSolver>(solver_type));
// Get the constructor function
Napi::Function cons = env.GetInstanceData<Napi::FunctionReference>()->Value();

// Create new instance
Napi::Object instance = cons.New({});
MICMClass* obj = Napi::ObjectWrap<MICMClass>::Unwrap(instance);

// Initialize the wrapper with config string using factory method
obj->micm_ = MICMWrapper::FromConfigString(config_string, solver_type);

return instance;
}
catch (const std::exception& e)
{
Napi::Error::New(env, e.what()).ThrowAsJavaScriptException();
return env.Null();
}
}

Expand Down Expand Up @@ -134,6 +196,8 @@ Napi::Object MICMClass::Init(Napi::Env env, Napi::Object exports)
InstanceMethod("createState", &MICMClass::CreateState),
InstanceMethod("solve", &MICMClass::Solve),
InstanceMethod("getSolverType", &MICMClass::GetSolverType),
StaticMethod("fromConfigPath", &MICMClass::FromConfigPath),
StaticMethod("fromConfigString", &MICMClass::FromConfigString),
});

Napi::FunctionReference* constructor = new Napi::FunctionReference();
Expand Down
4 changes: 4 additions & 0 deletions javascript/src/micm/micm.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ class MICMClass : public Napi::ObjectWrap<MICMClass>
static Napi::Object Init(Napi::Env env, Napi::Object exports);
MICMClass(const Napi::CallbackInfo& info);

// Static factory methods
static Napi::Value FromConfigPath(const Napi::CallbackInfo& info);
static Napi::Value FromConfigString(const Napi::CallbackInfo& info);

private:
std::unique_ptr<MICMWrapper> micm_;

Expand Down
31 changes: 28 additions & 3 deletions javascript/src/micm/micm_wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,16 @@ namespace musica_addon
// MICMWrapper Implementation
// ============================================================================

MICMWrapper::MICMWrapper(const std::string& config_path, int solver_type)
: micm_(nullptr),
MICMWrapper::MICMWrapper(musica::MICM* micm, int solver_type)
: micm_(micm),
solver_type_(solver_type)
{
}

std::unique_ptr<MICMWrapper> MICMWrapper::FromConfigPath(const std::string& config_path, int solver_type)
{
musica::Error error;
micm_ = musica::CreateMicm(config_path.c_str(), static_cast<musica::MICMSolver>(solver_type), &error);
musica::MICM* micm = musica::CreateMicm(config_path.c_str(), static_cast<musica::MICMSolver>(solver_type), &error);

if (!musica::IsSuccess(error))
{
Expand All @@ -42,6 +46,27 @@ namespace musica_addon
throw std::runtime_error(error_msg);
}
musica::DeleteError(&error);
return std::unique_ptr<MICMWrapper>(new MICMWrapper(micm, solver_type));
}

std::unique_ptr<MICMWrapper> MICMWrapper::FromConfigString(const std::string& config_string, int solver_type)
{
musica::Error error;
musica::MICM* micm = musica::CreateMicmFromConfigString(config_string.c_str(), static_cast<musica::MICMSolver>(solver_type), &error);

if (!musica::IsSuccess(error))
{
std::string error_msg = "Failed to create MICM solver from config string: ";
if (error.message_.value_ != nullptr)
{
error_msg += error.message_.value_;
musica::DeleteString(&error.message_);
}
musica::DeleteError(&error);
throw std::runtime_error(error_msg);
}
musica::DeleteError(&error);
return std::unique_ptr<MICMWrapper>(new MICMWrapper(micm, solver_type));
}

MICMWrapper::~MICMWrapper()
Expand Down
6 changes: 5 additions & 1 deletion javascript/src/micm/micm_wrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,18 @@ namespace musica_addon
class MICMWrapper
{
public:
MICMWrapper(const std::string& config_path, int solver_type);
~MICMWrapper();

// Static factory methods
static std::unique_ptr<MICMWrapper> FromConfigPath(const std::string& config_path, int solver_type);
static std::unique_ptr<MICMWrapper> FromConfigString(const std::string& config_string, int solver_type);

musica::State* CreateState(size_t number_of_grid_cells);
micm::SolverResult Solve(musica::State* state, double time_step);
int GetSolverType() const;

private:
MICMWrapper(musica::MICM* micm, int solver_type);
musica::MICM* micm_;
int solver_type_;
};
Expand Down
Loading
Loading