JavaScript implementation of the MusicBox atmospheric chemistry box model, powered by the MUSICA WebAssembly chemistry solver.
Works in both Node.js and browser environments. Accepts the same music-box v1 JSON config format as the Python package — no file I/O required when conditions are supplied inline.
npm install @ncar/music-boxEach example config is published with the package and can be imported directly as JSON:
import { MusicBox } from '@ncar/music-box';
import chapmanConfig from '@ncar/music-box/examples/chapman/my_config.json' with { type: 'json' };
const box = MusicBox.fromJson(chapmanConfig);
const results = await box.solve();
console.log(results);
// [{ 'time.s': 0, 'CONC.O3.mol m-3': 6.43e-6, ... }, ...]Available examples: analytical, chapman, flow_tube, carbon_bond_5, ts1.
import { MusicBox } from '@ncar/music-box';
const box = await MusicBox.fromJsonFile('./examples/chapman/my_config.json');
const results = await box.solve();
console.log(results);
// [{ 'time.s': 0, 'CONC.O3.mol m-3': 6.43e-6, ... }, ...]import { MusicBox } from '@ncar/music-box';
const config = {
'box model options': {
'chemistry time step [min]': 1.0,
'output time step [min]': 10.0,
'simulation length [hr]': 1.0,
},
conditions: {
data: [
{
headers: ['time.s', 'ENV.temperature.K', 'ENV.pressure.Pa',
'CONC.O3.mol m-3', 'CONC.O2.mol m-3'],
rows: [[0.0, 217.6, 1394.3, 6.43e-6, 0.162]],
},
{
headers: ['time.s', 'PHOTO.O2_1.s-1', 'PHOTO.O3_1.s-1'],
rows: [
[0, 1.47e-12, 4.25e-5],
[3600, 1.12e-13, 1.33e-6],
],
},
],
},
mechanism: {
// see config format docs for full mechanism structure
},
};
const box = MusicBox.fromJson(config);
const results = await box.solve();The conditions.data key accepts an array of {headers, rows} blocks — one block per logical data source, equivalent to one CSV file. This is identical to the format the Python implementation accepts via conditions.data.
{
"conditions": {
"data": [
{
"headers": ["time.s", "ENV.temperature.K", "ENV.pressure.Pa"],
"rows": [[0.0, 217.6, 1394.3]]
},
{
"headers": ["time.s", "PHOTO.O2_1.s-1", "PHOTO.O3_1.s-1"],
"rows": [
[0, 1.47e-12, 4.25e-5],
[3600, 1.12e-13, 1.33e-6]
]
}
]
}
}| Column | Example | Description |
|---|---|---|
ENV.temperature.K |
217.6 |
Air temperature in Kelvin. Step-interpolated. |
ENV.pressure.Pa |
1394.3 |
Air pressure in Pascals. Step-interpolated. |
CONC.<species>.mol m-3 |
6.43e-6 |
Species concentration. Applied at exact time only. |
PHOTO.<name>.s-1 |
1.47e-12 |
Photolysis rate. Step-interpolated. |
EMIS.<name>.<unit> |
0.001 |
Emission rate. Step-interpolated. |
LOSS.<name>.<unit> |
0.001 |
Loss rate. Step-interpolated. |
USER.<name>.<unit> |
1.0 |
User-defined rate parameter. Step-interpolated. |
CONC.* columns are treated as concentration events and applied only at their exact time. All other columns use step interpolation (hold the most recent value until the next time point).
import { MusicBox } from '@ncar/music-box';
// Create from a JSON object (Node.js and browser)
const box = MusicBox.fromJson(configObject);
// Create from a JSON file path (Node.js only)
const box = await MusicBox.fromJsonFile('/path/to/config.json');
// Run the simulation
const results = await box.solve();
// Returns: Array of output rows, e.g.:
// [{ 'time.s': 0, 'CONC.O3.mol m-3': 6.43e-6, ... }, ...]Extracts timing parameters from config['box model options'], converting all time units ([s], [sec], [min], [hr], [hour], [day]) to seconds.
import { parseBoxModelOptions } from '@ncar/music-box';
const { chemTimeStep, outputTimeStep, simulationLength, maxIterations } =
parseBoxModelOptions(config);Converts conditions.data blocks into a flat array of row objects for use by ConditionsManager.
import { parseConditions } from '@ncar/music-box';
const dataRows = parseConditions(config.conditions);
// [{ 'time.s': 0, 'ENV.temperature.K': 217.6, ... }, ...]Manages step interpolation of environmental conditions and collection of concentration events.
import { ConditionsManager, parseConditions } from '@ncar/music-box';
const mgr = new ConditionsManager(parseConditions(config.conditions));
// Step-interpolated temperature, pressure, and rate parameters at time t (seconds)
const { temperature, pressure, rateParams } = mgr.getConditionsAtTime(t);
// Concentration events: { time: { speciesName: value } }
const events = mgr.concentrationEvents;All npm commands are run from the repository root (where package.json lives):
npm install # install dependencies
npm test # run all tests (unit + integration)
npm run test:unit # unit tests only
npm run test:integration # integration tests only
npm run test:coverage # tests with coverage report
npm run build # build browser bundle → dist/music-box.bundle.jsmusic-box/
├── package.json ← npm metadata and scripts
├── package-lock.json
├── webpack.config.js ← browser bundle config
├── examples/ ← example configs (analytical, chapman, flow_tube, …)
│ ├── analytical/
│ │ ├── my_config.json
│ │ └── initial_conditions.csv
│ ├── chapman/
│ │ ├── my_config.json
│ │ ├── initial_concentrations.csv
│ │ └── conditions_Boulder.csv
│ └── …
├── javascript/
│ ├── src/
│ │ ├── index.js
│ │ ├── music_box.js
│ │ ├── config_parser.js
│ │ ├── conditions_manager.js
│ │ └── utils.js
│ └── tests/
│ ├── unit/
│ └── integration/
└── src/acom_music_box/ ← Python implementation