src/converter/ArtistooImport.js
import ModelDescription from "./ModelDescription.js"
import PixelsByCell from "../stats/PixelsByCell.js"
class ArtistooImport extends ModelDescription {
constructor( model ) {
super()
if( model.isCPM ){
this.C = model
this.simsettings = {}
this.mode = "CPM"
} else if ( model.isSimulation ){
this.sim = model
this.C = model.C
this.simsettings = model.conf
this.mode = "Simulation"
} else {
throw("Model must be a CPM or Simulation object!")
}
this.from = "an Artistoo model (" + this.mode + " class)"
this.generalWarning += "\nWarning: cannot automatically convert an entire " +
"Artistoo script. This converter handles anything in the configuration " +
"object of your " + this.mode + ", such as spatial settings, time settings " +
"and CPM parameters. It also handles the initial configuration. But " +
"if you perform extra actions between steps " +
"(such as dividing cells, killing cells, producing chemokines, etc.)," +
" these are not automatically added to the converted model. " +
"Please check your script manually for such actions; they should be " +
"added manually in the destination framework (please consult that framework's " +
"documentation to see how).\n\n"
this.build()
}
setModelInfo(){
this.modelInfo.title = "ArtistooImport"
this.modelInfo.desc = "Please add a description of your model here."
this.conversionWarnings.modelInfo.push(
"Cannot set model title and description automatically from an HTML page; " +
"please add these manually to your model."
)
}
setTimeInfo(){
this.timeInfo.start = 0
if( this.simsettings.hasOwnProperty("BURNIN") ){
this.timeInfo.start += this.simsettings["BURNIN"]
}
if( this.simsettings.hasOwnProperty( "RUNTIME" ) ){
this.timeInfo.stop = this.timeInfo.start + this.simsettings.RUNTIME
} else if (this.simsettings.hasOwnProperty( "RUNTIME_BROWSER" ) ){
this.timeInfo.stop = this.timeInfo.start + this.simsettings.RUNTIME_BROWSER
} else {
this.timeInfo.stop = 100
this.conversionWarnings.time.push(
"Could not find any information of runtime; setting the simulation to " +
"100 MCS for now. Please adjust manually."
)
}
this.timeInfo.duration = this.timeInfo.stop - this.timeInfo.start
if( this.timeInfo.duration < 0 ){
throw( "Error: I cannot go back in time; timeInfo.stop must be larger than timeInfo.start!")
}
}
setGridInfo(){
this.grid.ndim = this.C.grid.extents.length
this.grid.extents = this.C.grid.extents
this.grid.geometry = "square"
if( this.grid.ndim === 3 ){
this.grid.geometry = "cubic"
}
this.grid.neighborhood = { order: 2 }
const torus = this.C.grid.torus
let bounds = []
for( let t of torus ){
if(t){
bounds.push( "periodic" )
} else {
bounds.push( "noflux" )
}
}
this.grid.boundaries = bounds
}
setCellKindNames(){
this.cellKinds.count = undefined
// If there are simsettings, NRCELLS contains info on number of cellkinds.
/*if( this.simsettings.hasOwnProperty("NRCELLS" ) ){
this.cellKinds.count = this.simsettings.NRCELLS.length + 1
// Otherwise, try getting it from the constraint parameters.
} else {*/
let found = false
const constraints = this.C.getAllConstraints()
// If there's an adhesion constraint, we can get the info from the J matrix.
if( constraints.hasOwnProperty( "Adhesion" ) ){
found = true
this.cellKinds.count = this.C.getConstraint("Adhesion").conf.J.length
// Otherwise, loop through constraints to find a parameter starting
// with LAMBDA and use that.
} else {
for( let cn of Object.keys( constraints ) ){
let cc = this.C.getConstraint(cn).conf
// Find index of first param that starts with LAMBDA;
// returns -1 if there are none.
const lambdaIndex = Object.keys(cc).findIndex(
function (k) {
return ~k.indexOf("LAMBDA")
})
if (lambdaIndex > -1) {
// there is a lambda parameter, assume specified per cellkind
found = true
const parmName = Object.keys(cc)[lambdaIndex]
this.cellKinds.count = cc[parmName].length
}
if (found) {
break
}
}
// if we get here, still no success. Now try to read how many
// cellKinds there are from the initialized grid.
let kinds = {}
for( let cid of this.C.cellIDs() ){
if( !kinds.hasOwnProperty( this.C.cellKind(cid) ) ){
kinds[this.C.cellKind(cid)] = true
}
}
this.cellKinds.count = Object.keys( kinds ).length + 1
this.conversionWarnings.cells.push(
"Could not find how many CellKinds there are automatically! " +
"Counting the number of cellKinds on the initialized grid, but " +
"if the simulation introduces new cellKinds only after initialization " +
"then the output will be wrong. Please check manually! " +
"As a workaround, you can add an Adhesion constraint to the model" +
"with all-zero contact energies; this will not change the model " +
"but will define the number of cellkinds properly."
)
//}
}
// if still undefined, don't add any except background an add a warning.
if( typeof this.cellKinds.count === "undefined" ){
this.cellKinds.count = 1
this.conversionWarnings.cells.push(
"Could not find how many CellKinds there are automatically! " +
"Ignoring everything except background, output will be wrong. " +
"As a workaround, you can add an Adhesion constraint to the model" +
"with all-zero contact energies; this will not change the model " +
"but will define the number of cellkinds properly."
)
}
this.cellKinds.index2name = {}
this.cellKinds.name2index = {}
for( let k = 0; k < this.cellKinds.count; k++ ){
if( k === 0 ){
this.cellKinds.index2name[k] = "medium"
this.cellKinds.name2index["medium"] = k
} else {
this.cellKinds.index2name[k] = "celltype" + k
this.cellKinds.name2index["celltype"+k ] = k
}
}
// empty object for each cellkind.
this.cellKinds.properties = {}
for( let n of Object.keys( this.cellKinds.name2index ) ){
this.cellKinds.properties[n] = {}
}
}
setCPMGeneral(){
// Random Seed
this.kinetics.seed = this.C.conf.seed
// Temperature
this.kinetics.T = this.C.conf.T
}
setConstraints(){
const constraints = this.C.getAllConstraints()
for( let cName of Object.keys( constraints ) ){
this.constraints.constraints[cName] = []
let index = 0
while( typeof this.C.getConstraint( cName, index ) !== "undefined"){
let cc = this.C.getConstraint( cName, index ).conf
this.constraints.constraints[cName].push( cc )
index++
}
}
}
setGridConfiguration(){
// If the supplied model is a Simulation; this is the most robust method
// because we can reset the model and export the grid configuration
// directly after initializeGrid has been called.
if( typeof this.sim !== "undefined" ){
// stop the simulation
this.sim.toggleRunning()
// remove any cells from the grid and reinitialize
this.sim.C.reset()
this.sim.initializeGrid()
this.readPixelsByCell()
// Reset time to just after initialisation
this.sim.C.time -= this.sim.time
this.sim.time -= this.sim.time
this.sim.runBurnin()
this.sim.toggleRunning()
} else {
this.conversionWarnings.init.push(
"You have supplied a CPM rather than a Simulation object; " +
"reading the initial settings directly from the CPM. This is " +
"slightly less robust than reading it from the Simulation since " +
"I cannot go back to time t = 0 to read the exact initial setup."
)
this.readPixelsByCell()
}
}
readPixelsByCell(){
const cellPix = this.C.getStat( PixelsByCell )
for( let cid of Object.keys( cellPix ) ){
if( cellPix[cid].length > 0 ) {
const cki = this.C.cellKind(cid)
this.setup.init.push({
setter: "pixelSet",
kind: cki,
kindName: this.getKindName(cki),
cid: cid,
pixels: cellPix[cid]
})
}
}
}
}
export default ArtistooImport