From 601082705339cc53e071ebc7268c8bdf3d2f44e1 Mon Sep 17 00:00:00 2001 From: BryanRumsey <44621966+BryanRumsey@users.noreply.github.com> Date: Mon, 2 May 2022 14:13:31 -0400 Subject: [PATCH 001/110] Updated version to v2.4.8 --- __version__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/__version__.py b/__version__.py index a9f7c69a73..b6b2f10e76 100644 --- a/__version__.py +++ b/__version__.py @@ -5,7 +5,7 @@ # @website https://github.com/stochss/stochss # ============================================================================= -__version__ = '2.4.7' +__version__ = '2.4.8' __title__ = 'StochSS' __description__ = 'StochSS is an integrated development environment (IDE) \ for simulation of biochemical networks.' From 17e6bae18051e9c55af8ac89e398543ac48b5643 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Mon, 2 May 2022 14:57:37 -0400 Subject: [PATCH 002/110] Added support for all SpatialPy modes. --- client/model-view/model-view.js | 32 +++++++++++++++++++++++++------- client/model-view/modelView.pug | 15 +++++++++++++-- 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/client/model-view/model-view.js b/client/model-view/model-view.js index ee749e91a9..7267fd628a 100644 --- a/client/model-view/model-view.js +++ b/client/model-view/model-view.js @@ -46,6 +46,9 @@ module.exports = View.extend({ 'change [data-hook=all-continuous]' : 'setDefaultMode', 'change [data-hook=all-discrete]' : 'setDefaultMode', 'change [data-hook=advanced]' : 'setDefaultMode', + 'change [data-hook=spatial-continuous]' : 'setDefaultMode', + 'change [data-hook=spatial-discrete]' : 'setDefaultMode', + 'change [data-hook=spatial-discrete-concentration]' : 'setDefaultMode', 'change [data-hook=edit-volume]' : 'updateVolumeViewer', 'click [data-hook=collapse-mv-advanced-section]' : 'changeCollapseButtonText', 'click [data-hook=collapse-system-volume]' : 'changeCollapseButtonText' @@ -76,14 +79,27 @@ module.exports = View.extend({ this.setReadOnlyMode("model-mode"); this.setReadOnlyMode("system-volume"); }else { - if(this.model.defaultMode === "" && !this.model.is_spatial){ - this.getInitialDefaultMode(); + if(this.model.defaultMode === ""){ + if(this.model.is_spatial) { + this.model.defaultMode = "discrete"; + $(this.queryByHook("spatial-discrete")).prop('checked', true); + }else{ + this.getInitialDefaultMode(); + } }else { - let dataHooks = {'continuous':'all-continuous', 'discrete':'all-discrete', 'dynamic':'advanced'}; - $(this.queryByHook(dataHooks[this.model.defaultMode])).prop('checked', true); if(this.model.is_spatial) { - $(this.queryByHook("advanced-model-mode")).css("display", "none"); + let dataHooks = { + 'continuous':'spatial-continuous', + 'discrete':'spatial-discrete', + 'discrete-concentration':'spatial-discrete-concentration' + }; + $(this.queryByHook(dataHooks[this.model.defaultMode])).prop('checked', true); + $(this.queryByHook("model-mode-container")).css("display", "none"); $(this.queryByHook("system-volume-container")).css("display", "none"); + }else{ + let dataHooks = {'continuous':'all-continuous', 'discrete':'all-discrete', 'dynamic':'advanced'}; + $(this.queryByHook(dataHooks[this.model.defaultMode])).prop('checked', true); + $(this.queryByHook("spatial-model-mode-container")).css("display", "none"); } } this.model.reactions.on("change", (reactions) => { @@ -319,8 +335,10 @@ module.exports = View.extend({ let prevMode = this.model.defaultMode; this.model.defaultMode = e.target.dataset.name; this.speciesView.defaultMode = e.target.dataset.name; - this.setAllSpeciesModes(prevMode); - this.toggleVolumeContainer(); + if(!this.model.is_spatial) { + this.setAllSpeciesModes(prevMode); + this.toggleVolumeContainer(); + } }, setInitialDefaultMode: function (modal, mode) { var dataHooks = {'continuous':'all-continuous', 'discrete':'all-discrete', 'dynamic':'advanced'}; diff --git a/client/model-view/modelView.pug b/client/model-view/modelView.pug index 9284a249c5..def5cf4d6c 100644 --- a/client/model-view/modelView.pug +++ b/client/model-view/modelView.pug @@ -32,17 +32,28 @@ div#model-view hr - div.row + div.row(data-hook="model-mode-container") div.col-sm-4 input.mr-2(type="radio" name="default-mode" data-hook="all-continuous" data-name="continuous") span Concentration div.col-sm-4 input.mr-2(type="radio" name="default-mode" data-hook="all-discrete" data-name="discrete") span Population - div.col-sm-4(data-hook="advanced-model-mode") + div.col-sm-4 input.mr-2(type="radio" name="default-mode" data-hook="advanced" data-name="dynamic") span Hybrid Concentration/Population + div.row(data-hook="spatial-model-mode-container") + div.col-sm-4 + input.mr-2(type="radio" name="default-mode" data-hook="spatial-continuous" data-name="continuous") + span Concentration + div.col-sm-4 + input.mr-2(type="radio" name="default-mode" data-hook="spatial-discrete" data-name="discrete") + span Population (outpust: absolute value) + div.col-sm-4 + input.mr-2(type="radio" name="default-mode" data-hook="spatial-discrete-concentration" data-name="discrete-concentration") + span Population (outputs: scaled by volume) + div.tab-pane(id="view-model-mode" data-hook="view-model-mode") hr From 3a1e8a6d43a123e293f6dd1aea94a3ee16e2dff9 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Mon, 2 May 2022 14:58:29 -0400 Subject: [PATCH 003/110] Added backward compatability support for spatial modes. --- stochss/handlers/util/stochss_model.py | 2 ++ stochss/handlers/util/stochss_spatial_model.py | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/stochss/handlers/util/stochss_model.py b/stochss/handlers/util/stochss_model.py index a090934d24..f841d6541c 100644 --- a/stochss/handlers/util/stochss_model.py +++ b/stochss/handlers/util/stochss_model.py @@ -385,6 +385,8 @@ def convert_to_spatial(self): if self.model is None: model = self.load() model['is_spatial'] = True + if model['defaultMode'] == "dynamic": + model['defaultMode'] == "discrete-concentration" if "timestepSize" not in self.model['modelSettings'].keys(): self.model['modelSettings']['timestepSize'] = 1e-5 if "domain" not in model.keys(): diff --git a/stochss/handlers/util/stochss_spatial_model.py b/stochss/handlers/util/stochss_spatial_model.py index a32a083a4f..c2932c10d6 100644 --- a/stochss/handlers/util/stochss_spatial_model.py +++ b/stochss/handlers/util/stochss_spatial_model.py @@ -391,6 +391,8 @@ def convert_to_model(self): if self.model is None: s_model = self.load() s_model['is_spatial'] = False + if s_model['defaultMode'] == "discrete-concentration": + s_model['defaultMode'] == "dynamic" if ".wkgp" in self.path: wkgp_path = self.get_dir_name() wkgp_path, _ = self.get_unique_path(name=self.get_file(path=wkgp_path), @@ -635,6 +637,8 @@ def load(self): self.model['name'] = self.get_name() if not self.model['defaultMode']: self.model['defaultMode'] = "discrete" + elif self.model['defaultMode'] == "dynamic": + self.model['defaultMode'] = "discrete-concentration" if "timestepSize" not in self.model['modelSettings'].keys(): self.model['modelSettings']['timestepSize'] = 1e-5 self.__update_domain() From 40d571f396992e30ddf462585d7016ba952e488e Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Mon, 2 May 2022 15:16:41 -0400 Subject: [PATCH 004/110] Fixed spelling error. Only log errors if they exist. --- stochss/handlers/file_browser.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/stochss/handlers/file_browser.py b/stochss/handlers/file_browser.py index 7bccf87585..0dde9679a6 100644 --- a/stochss/handlers/file_browser.py +++ b/stochss/handlers/file_browser.py @@ -709,7 +709,8 @@ async def get(self): log.debug(f"Exec command: {exec_cmd}") pipe = subprocess.Popen(exec_cmd, stdout=subprocess.PIPE, text=True) results, error = pipe.communicate() - log.error(f"Errors trown by the subprocess: {error}") + if error is not None: + log.error(f"Errors thrown by the subprocess: {error}") resp = json.loads(results) log.debug(f"Response: {resp}") self.write(resp) From 8fe94483e65efe13b5172b7bc1e4fc9ec4e49fad Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Wed, 22 Jun 2022 12:05:59 -0400 Subject: [PATCH 005/110] Added support for new ode propensity functions. --- stochss/handlers/util/stochss_model.py | 7 ++++++- stochss/handlers/util/stochss_spatial_model.py | 5 +++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/stochss/handlers/util/stochss_model.py b/stochss/handlers/util/stochss_model.py index a090934d24..0bbe55baa4 100644 --- a/stochss/handlers/util/stochss_model.py +++ b/stochss/handlers/util/stochss_model.py @@ -165,15 +165,18 @@ def __convert_reactions(self, model): if reaction['reactionType'] == "custom-propensity": rate = None propensity = reaction['propensity'] + ode_propensity = reaction['odePropensity'] else: rate = g_parameters[reaction['rate']['name']] propensity = None + ode_propensity = None reactants, products = self.__convert_stoich_species(reaction=reaction) g_reaction = Reaction(name=reaction['name'], reactants=reactants, products=products, rate=rate, - propensity_function=propensity) + propensity_function=propensity, + ode_propensity_function=ode_propensity) model.add_reaction(g_reaction) except KeyError as err: message = "Reactions are not properly formatted or " @@ -295,6 +298,8 @@ def __update_reactions(self): if "reactions" not in self.model.keys(): return for reaction in self.model['reactions']: + if "odePropensity" not in reaction.keys(): + reaction['odePropensity'] = reaction['propensity'] try: if reaction['rate'].keys() and isinstance(reaction['rate']['expression'], str): expression = ast.literal_eval(reaction['rate']['expression']) diff --git a/stochss/handlers/util/stochss_spatial_model.py b/stochss/handlers/util/stochss_spatial_model.py index a32a083a4f..4b9b0cf513 100644 --- a/stochss/handlers/util/stochss_spatial_model.py +++ b/stochss/handlers/util/stochss_spatial_model.py @@ -246,9 +246,11 @@ def __convert_reactions(self, model, type_ids): if reaction['reactionType'] != "custom-propensity": rate = s_params[reaction['rate']['name']] propensity = None + ode_propensity = None else: rate = None propensity = reaction['propensity'] + ode_propensity = reaction['odePropensity'] types = [type_ids[d_type] for d_type in reaction['types']] if len(types) == len(type_ids): types = None @@ -257,6 +259,7 @@ def __convert_reactions(self, model, type_ids): products=products, rate=rate, propensity_function=propensity, + ode_propensity_function=ode_propensity, restrict_to=types) model.add_reaction(s_reaction) except KeyError as err: @@ -647,6 +650,8 @@ def load(self): diff = 0.0 if "diffusionCoeff" not in species.keys() else species['diffusionCoeff'] species['diffusionConst'] = diff for reaction in self.model['reactions']: + if "odePropensity" not in reaction.keys(): + reaction['odePropensity'] = reaction['propensity'] if "types" not in reaction.keys(): reaction['types'] = list(range(1, len(self.model['domain']['types']))) return self.model From 7161ced0b9d9b82b93e27983a47d29e150cd1085 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Wed, 22 Jun 2022 12:16:11 -0400 Subject: [PATCH 006/110] Fixed updateReactionType function. Added function to build massaction propensities. Added ode propensity support. --- client/models/reaction.js | 59 +++++++++++++++++++++++++++++---------- 1 file changed, 45 insertions(+), 14 deletions(-) diff --git a/client/models/reaction.js b/client/models/reaction.js index 3edcbe8941..262103d508 100644 --- a/client/models/reaction.js +++ b/client/models/reaction.js @@ -27,9 +27,8 @@ module.exports = State.extend({ props: { compID: 'number', name: 'string', - reactionType: 'string', - summary: 'string', massaction: 'boolean', + odePropensity: 'string', propensity: 'string', annotation: 'string', types: 'object' @@ -42,29 +41,58 @@ module.exports = State.extend({ products: StoichSpecies }, session: { - selected: { + hasConflict: { type: 'boolean', - default: true, + default: false, }, - hasConflict: { + maODEPropensity: 'string', + maPropensity: 'string', + mirrorPropensities: 'boolean', + reactionType: 'string', + selected: { type: 'boolean', default: false, }, + summary: 'string' }, initialize: function (attrs, options) { var self = this; State.prototype.initialize.apply(this, arguments); - if(!this.reactionType.startsWith('custom')) { - let reactionType = this.updateReactionType(); - if(this.reactionType !== reactionType){ - this.reactionType = reactionType - this.buildSummary() + this.reactionType = this.updateReactionType(); + this.mirrorPropensities = this.propensity === this.odePropensity; + this.buildSummary(); + this.buildMAPropensities(); + this.checkModes(); + this.on('change-reaction', () => { + this.buildSummary(); + this.buildMAPropensities(); + this.checkModes(); + }); + }, + buildMAPropensities: function () { + if(this.reactionType === "custom-propensity") { return } + var odePropensity = this.rate.name; + var propensity = this.rate.name; + + this.reactants.forEach((stoichSpecies) => { + let name = stoichSpecies.specie.name; + if(stoichSpecies.ratio == 2) { + odePropensity += ` * ${name} * ${name}`; + propensity = `0.5 * ${propensity} * ${name} * (${name} - 1) / vol`; + }else{ + odePropensity += ` * ${name}`; + propensity += ` * ${name}`; } + }) + + let order = this.reactants.length; + if(order == 2) { + propensity += " / vol"; + }else if(order == 0) { + propensity += " * vol" } - this.on('change-reaction', function () { - self.buildSummary(); - self.checkModes(); - }); + this.maODEPropensity = odePropensity; + this.maPropensity = propensity; }, buildSummary: function () { var summary = ""; @@ -137,6 +165,9 @@ module.exports = State.extend({ this.hasConflict = Boolean(hasContinuous && (hasDynamic || hasDiscrete)) }, updateReactionType: function () { + if(!this.massaction) { + return "custom-propensity" + } let numReactants = this.reactants.length let numProducts = this.products.length let prodRatio1 = numProducts > 0 ? this.products.models[0].ratio : 0 From ff9c1c43af1bab14083fb9501162cbd4c3a07b99 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Wed, 22 Jun 2022 12:18:43 -0400 Subject: [PATCH 007/110] Added ode propensity and massaction propensity support. Refactored getDefaultSpecies to use as many unique species as possible when creating reactants and products. --- client/models/reactions.js | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/client/models/reactions.js b/client/models/reactions.js index eb4e1d6861..f609f339c5 100644 --- a/client/models/reactions.js +++ b/client/models/reactions.js @@ -31,13 +31,13 @@ Reactions = Collection.extend({ addReaction: function (reactionType, stoichArgs, types) { var id = this.parent.getDefaultID(); var name = this.getDefaultName(); - var massaction = reactionType === 'custom-massaction'; + var massaction = reactionType !== 'custom-propensity'; var reaction = new Reaction({ compID: id, name: name, - reactionType: reactionType, massaction: massaction, propensity: '', + odePropensity: '', annotation: '', types: types, reactants: stoichArgs.reactants, @@ -47,9 +47,12 @@ Reactions = Collection.extend({ this.setDefaultSpecieForStoichSpecies(reaction.products); if(reactionType !== 'custom-propensity') reaction.rate = this.getDefaultRate(); - reaction.buildSummary() + reaction.buildSummary(); + reaction.buildMAPropensities(); + reaction.reactionType = reactionType; + reaction.selected = true; this.add(reaction); - this.parent.updateValid() + this.parent.updateValid(); return reaction; }, getDefaultName: function () { @@ -64,11 +67,14 @@ Reactions = Collection.extend({ }, setDefaultSpecieForStoichSpecies: function (stoichSpecies) { stoichSpecies.forEach(function (stoichSpecie) { - stoichSpecie.specie = this.getDefaultSpecie(); + stoichSpecie.specie = this.getDefaultSpecie(stoichSpecies.indexOf(stoichSpecie)); }, this); }, - getDefaultSpecie: function () { - var specie = this.parent.species.at(0); + getDefaultSpecie: function (index) { + if(this.parent.species.length <= index) { + index -= this.parent.species.length; + } + var specie = this.parent.species.at(index); return specie; }, getDefaultRate: function () { From 3ba02110eff2986e871dbe449190cec531b50b0b Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Wed, 22 Jun 2022 12:21:28 -0400 Subject: [PATCH 008/110] Added support for Collection.get using compID as the key. --- client/models/parameters.js | 1 + client/models/species.js | 1 + 2 files changed, 2 insertions(+) diff --git a/client/models/parameters.js b/client/models/parameters.js index 520418a72d..2279bc59ee 100644 --- a/client/models/parameters.js +++ b/client/models/parameters.js @@ -24,6 +24,7 @@ var Collection = require('ampersand-collection'); module.exports = Collection.extend({ model: Parameter, + indexes: ['compID'], addParameter: function () { var id = this.parent.getDefaultID(); var name = this.getDefaultName(); diff --git a/client/models/species.js b/client/models/species.js index 2e822cfc5b..dcba8eadc2 100644 --- a/client/models/species.js +++ b/client/models/species.js @@ -24,6 +24,7 @@ var Collection = require('ampersand-collection'); module.exports = Collection.extend({ model: Specie, + indexes: ['compID'], addSpecie: function (types) { var id = this.parent.getDefaultID(); var name = this.getDefaultName(); From 02ca9571abd9dfcdda5f74359cb78cc8b50c9768 Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Wed, 22 Jun 2022 12:22:58 -0400 Subject: [PATCH 009/110] Added optional arg for ratio when adding new reactants or products. --- client/models/stoich-species.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/models/stoich-species.js b/client/models/stoich-species.js index 70cff4ecb5..4fc9006f68 100644 --- a/client/models/stoich-species.js +++ b/client/models/stoich-species.js @@ -32,12 +32,12 @@ module.exports = Collection.extend({ this.baseModel = this.parent.collection.parent; this.baseModel.species.trigger('stoich-species-change'); }, - addStoichSpecie: function (specieName) { + addStoichSpecie: function (specieName, {ratio=1}={}) { var specie = this.parent.collection.parent.species.filter(function (specie) { return specie.name === specieName; })[0]; var stoichSpecie = new StoichSpecie({ - ratio: 1 + ratio: ratio }); stoichSpecie.specie = specie; this.add(stoichSpecie); From 24ffd589bb268ba5739d4dd138facfbe792d1f1f Mon Sep 17 00:00:00 2001 From: Bryan Rumsey Date: Wed, 22 Jun 2022 12:24:04 -0400 Subject: [PATCH 010/110] Added support for a disabled attribute. --- client/views/input.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/client/views/input.js b/client/views/input.js index e4531e0732..3e2ba786cc 100644 --- a/client/views/input.js +++ b/client/views/input.js @@ -24,6 +24,7 @@ module.exports = AmpersandInputView.extend({ label: 'string', modelKey: 'string', valueType: 'string', + disabled: 'boolean' }, events: { 'input input' : 'changeInputHandler', @@ -32,11 +33,12 @@ module.exports = AmpersandInputView.extend({ AmpersandInputView.prototype.initialize.apply(this, arguments); }, render: function () { + let disabled = this.disabled ? "disabled" : ""; if(this.label) { this.template = [ '