Skip to content

Commit

Permalink
mt-draft
Browse files Browse the repository at this point in the history
  • Loading branch information
metelkin committed Jul 15, 2024
1 parent 5a41024 commit 5e5a140
Show file tree
Hide file tree
Showing 7 changed files with 601 additions and 1 deletion.
3 changes: 2 additions & 1 deletion cases/0-hello-world/src/index.heta
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,5 @@ graph #export {
};
summary0 #export {
format: Summary
};
};
#export { spaceFilter: mm, format: MT };
2 changes: 2 additions & 0 deletions src/browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ global.compiledTemplates = {
'summary.md.njk': nunjucksEnv.getTemplate('summary.md.njk'),
'julia-model.jl.njk': nunjucksEnv.getTemplate('julia-model.jl.njk'),
'julia-run.jl.njk': nunjucksEnv.getTemplate('julia-run.jl.njk'),
'mt-model.jl.njk': nunjucksEnv.getTemplate('mt-model.jl.njk'),
'matlab-model.m.njk': nunjucksEnv.getTemplate('matlab-model.m.njk'),
'matlab-param.m.njk': nunjucksEnv.getTemplate('matlab-param.m.njk'),
'matlab-run.m.njk': nunjucksEnv.getTemplate('matlab-run.m.njk'),
Expand Down Expand Up @@ -58,6 +59,7 @@ Container._exportClasses = {
AnotherXLSX: require('./another-xlsx-export'),
Matlab: require('./matlab-export'),
Julia: require('./julia-export'),
MT: require('./mt-export'),
Dot: require('./dot-export'),
Summary: require('./summary-export'),
};
Expand Down
2 changes: 2 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ global.compiledTemplates = {
'summary.md.njk': nunjucksEnv.getTemplate('summary.md.njk'),
'julia-model.jl.njk': nunjucksEnv.getTemplate('julia-model.jl.njk'),
'julia-run.jl.njk': nunjucksEnv.getTemplate('julia-run.jl.njk'),
'mt-model.jl.njk': nunjucksEnv.getTemplate('mt-model.jl.njk'),
'matlab-model.m.njk': nunjucksEnv.getTemplate('matlab-model.m.njk'),
'matlab-param.m.njk': nunjucksEnv.getTemplate('matlab-param.m.njk'),
'matlab-run.m.njk': nunjucksEnv.getTemplate('matlab-run.m.njk'),
Expand Down Expand Up @@ -47,6 +48,7 @@ Container._exportClasses = {
AnotherXLSX: require('./another-xlsx-export'),
Matlab: require('./matlab-export'),
Julia: require('./julia-export'),
MT: require('./mt-export'),
Dot: require('./dot-export'),
Summary: require('./summary-export'),
};
Expand Down
61 changes: 61 additions & 0 deletions src/mt-export/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/* global compiledTemplates */
const { AbstractExport } = require('../abstract-export');
const pkg = require('../../package');
const { ajv } = require('../utils');
const { re } = require('mathjs');
require('./namespace');

const schema = {
type: 'object',
properties: {
}
};

class MTExport extends AbstractExport {
constructor(q = {}, isCore = false){
super(q, isCore);

// check arguments here
let logger = this._container.logger;
let valid = MTExport.isValid(q, logger);
if (!valid) { this.errored = true; return; }
}
get className(){
return 'MTExport';
}
get format(){
return 'MT';
}
static get validate(){
return ajv.compile(schema);
}
get requireConcrete() {
return true;
}
get defaultFilepath() {
return 'mt';
}
// skipVersionCode means that the version will not be printed in output
// this is required for autotests
makeText(skipVersionCode = false) {
//let logger = this._container.logger;

let results = this.selectedNamespaces().map(([spaceName, ns]) => {
let image = ns.getMTImage();
let content = this.getModelCode(image);

return {
content: content,
pathSuffix: `/${spaceName}.jl`,
type: 'text'
};
});

return results;
}
getModelCode(image = []){
return compiledTemplates['mt-model.jl.njk'].render(image);
}
}

module.exports = MTExport;
120 changes: 120 additions & 0 deletions src/mt-export/namespace.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
const { Namespace } = require('../namespace');
const { uniqBy } = require('../utils');

Namespace.prototype.getMTImage = function() {
// constants
let constants = this
.selectByInstanceOf('Const');
// ODE variables
let dynamicRecords = this
.selectByInstanceOf('Record')
.filter((x) => x.isDynamic);
// currently we output all records
let extendedRuleRecords = this
.sortExpressionsByContext('ode_', true)
.filter((x) => x.isExtendedRule);
let staticRecords = this
.selectByInstanceOf('Record')
.filter((x) => !x.isDynamic && !x.isRule);
// RHS of ODE
let rhs = dynamicRecords.map((record) => {
return record.backReferences.map((ref, i) => {
if (ref.stoichiometry === -1) {
var st = '-';
} else if (ref.stoichiometry < 0) {
st = ref.stoichiometry + '*';
} else if (ref.stoichiometry === 1) {
st = i === 0 ? '' : '+';
} else { // ref.stoichiometry >= 0
st = i === 0 ? ref.stoichiometry + '*' : '+' + ref.stoichiometry + '*';
}

// XXX this is wrong solution because it results in problem d(comp1*S1)/dt = r1*comp1
let isCompartmentRequired = ref._process_.className === 'Process'
&& record.instanceOf('Species')
&& !record.isAmount;
if (isCompartmentRequired) {
return st + ref.process + '*' + record.compartment;
} else {
return st + ref.process;
}
}).join('');
});

// initialize at start records
let initRecordsRaw = this
.sortExpressionsByContext('start_')
.filter((x) => x.instanceOf('Record') && (x.assignments['start_'] !== undefined || x.isRule));

let initRecords = initRecordsRaw;

// select only rules to calculate ode
// TODO: maybe it is betted to calculate only active Processes
let odeDeps = this
.selectByInstanceOf('Process')
.map((x) => x.id);
let odeRules = _minimalRuleList(extendedRuleRecords, odeDeps);

// other switchers
let events = this
.selectByInstanceOf('_Switcher')
.map((switcher) => {
let affect = this.toArray()
.filter((x) => {
return x.instanceOf('Record')
&& x.assignments !== undefined
&& x.assignments[switcher.id] !== undefined;
});

// find all unique dependencies inside assignments
let affectDeps = [];
affect.forEach((x) => {
let dep = x.dependOn(switcher.id, true);
affectDeps.push(...dep);
});

// select rules required for affect
let affectRules = _minimalRuleList(extendedRuleRecords, uniqBy(affectDeps));

// find all unique dependencies inside trigger
let triggerDeps = switcher.trigger ? switcher.trigger.dependOn() : [];
// select rules required for switcher
let triggerRules = _minimalRuleList(extendedRuleRecords, uniqBy(triggerDeps));

return {
switcher,
triggerRules,
affect,
affectRules
};
});

let pTranslatorObject = {};
constants.forEach((constant, i) => {
pTranslatorObject[constant.id] = `__constants__[${i+1}]`;
});

return {
namespace: this,
constants,
dynamicRecords,
staticRecords,
rhs,
initRecords,
extendedRuleRecords,
odeRules,
events,
pTranslator: pTranslatorObject,
};
};

// select sub-array from rulesList which describes deps
function _minimalRuleList(rulesList, deps = []){
// calculate number of rules to include
let rulesListIds = rulesList.map((x) => x.id);
let rulesListNum = deps.map((x) => rulesListIds.indexOf(x));
let rulesMaxIndex = Math.max(...rulesListNum);

// select rules required
return rulesList.slice(0, rulesMaxIndex + 1);
}
Loading

0 comments on commit 5e5a140

Please sign in to comment.