From 924c396300b2ff95215aa59b9a970ac0e7b454d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cerven=C3=BD?= Date: Sun, 16 Aug 2020 19:52:20 +0200 Subject: [PATCH 01/47] Initial version of PSO --- PP-GrowthOptimizer.js | 66 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 62 insertions(+), 4 deletions(-) diff --git a/PP-GrowthOptimizer.js b/PP-GrowthOptimizer.js index 4f50be4..4c7cc15 100644 --- a/PP-GrowthOptimizer.js +++ b/PP-GrowthOptimizer.js @@ -7,6 +7,7 @@ var UserDefinedProtocol = { // -optimizer parameters controlledParameter: 'none', controlledParameterSteps: [[ 1100, 25 ], [ 440, 25 ], [ 55, 25 ]], + particleSwarmOptimizer: true, // -optimizer stability check growthStatistics: true, regressionODType: 680, @@ -28,7 +29,7 @@ var UserDefinedProtocol = { } /* global - importPackage, java, Packages, theGroup, theAccessory, theExperiment, theLogger, ProtoConfig, ETrendFunction, result: true + importPackage, java, Packages, theServer, theGroup, theAccessory, theExperiment, theLogger, ProtoConfig, ETrendFunction, result: true */ /** @@ -39,7 +40,7 @@ var UserDefinedProtocol = { * @copyright Jan Červený 2020(c) * @license MIT * @version 3.3.0 - * @modified 14.8.2020 (JaCe) + * @modified 16.8.2020 (JaCe) * * @notes For proper functionality of the script "OD Regulator" protocol has to be disabled as well as chosen * controlled accessory protocols (i.e. Lights, Thermoregulation, GMS, Stirrer). @@ -220,6 +221,9 @@ function round (number, decimals) { // Rounding specific decimal point number return +(Math.round(number + 'e+' + decimals) + 'e-' + decimals) } +function getRandomOnInterval(min, max) { + return Math.random() * (max - min) + min; +} function debugLogger (message, status) { if ((status === undefined) || (status === 1) || (status === 'on')) { theLogger.info('[' + theGroup.getName() + '] ' + message) @@ -375,7 +379,7 @@ function controlPump () { if ((stepDuration[stepCounter] > 0) && UserDefinedProtocol.growthStatistics) { var DHCapacity = (Math.floor(stepDuration[stepCounter] / UserDefinedProtocol.ODReadoutInterval) - 3) > 0 ? (Math.floor(stepDuration[stepCounter] / UserDefinedProtocol.ODReadoutInterval) - 3) : 60 var regCoefExp = odSensorRegression.getDataHistory().regression(ETrendFunction.EXP, Math.ceil(DHCapacity - (UserDefinedProtocol.growthRateEvalFrac ? DHCapacity * (UserDefinedProtocol.growthRateEvalFrac / 100) : UserDefinedProtocol.growthRateEvalDelay / UserDefinedProtocol.ODReadoutInterval))) - debugLogger('Growth parameters: ' + regCoefExp.join(', ')) + debugLogger('Growth parameters: A=' + regCoefExp[0] +', B=' + regCoefExp[1] + ', R2=' + regCoefExp[2]) if (Number(regCoefExp[2]) >= UserDefinedProtocol.regressionCoDMin / 100) { stepDoublingTime[stepCounter] = (1 / (Number(regCoefExp[1]) * 3600 * 10)) * Math.LN2 theAccessory.context().put('stepCounter', ++stepCounter) @@ -419,7 +423,9 @@ function controlPump () { theAccessory.context().put('modeStabilized', 1) // changeCounter = theAccessory.context().getInt('changeCounter', 0) theExperiment.addEvent('*** Stabilized doubling time Dt (' + theGroup.getAccessory('thermo.thermo-reg').getValue() + String.fromCharCode(176) + 'C, ' + theAccessory.context().getString('controlledParameterText', 'no parameter') + ') is ' + round(stepDoublingTimeAvg, 2) + String.fromCharCode(177) + round(stepDoublingTimeIC95, 2) + ' h (IC95)') - if (UserDefinedProtocol.controlledParameterSteps.length > 1) { + if (UserDefinedProtocol.particleSwarmOptimizer) { + controlParameter(UserDefinedProtocol.controlledParameter, PSO(stepDoublingTimeAvg)) + } else if (UserDefinedProtocol.controlledParameterSteps.length > 1) { if (changeCounter < (UserDefinedProtocol.controlledParameterSteps.length - 1)) { controlParameter(UserDefinedProtocol.controlledParameter, UserDefinedProtocol.controlledParameterSteps[++changeCounter]) theAccessory.context().put('changeCounter', changeCounter) @@ -453,3 +459,55 @@ function controlPump () { return null // pump not influenced } } +// PSO implementation +function PSO (particleFitness) { + /** + * Particle Swarm Optimization. + * + * Changes parameter(s) in optimal way as evaluated by PSO algorithm. It requires actual fitness of the particle that is expected to be minimized, e.g. doubling time. + * + * @param {number} particleFitness Fitness of the particle. + * + * @return {array} New parameters / conditions. + */ + theAccessory.context().put('particleFitness', particleFitness) + var parameterSearchRange = [15, 35] + var particlePosition = theAccessory.context().get('particlePosition', UserDefinedProtocol.controlledParameterSteps[0]) + var particleBestPosition = theAccessory.context().get('particleBestPosition', particlePosition) + var particleBestFitness = theAccessory.context().get('particleBestFitness', particleFitness) + var particleStep = theAccessory.context().get('particleStep', 0.2 * parameterSearchRange[1] - parameterSearchRange[0]) // TODO change to multiparameter + var swarmLeader = theServer.getGroupByName('DoAB-PBR01-group') // TODO add identifier from UserDefinedProtocol instead + var swarmBestPosition = swarmLeader.getAccessory('pumps.pump-5').context().get('swarmBestPosition', particlePosition) + var swarmBestFitness = swarmLeader.getAccessory('pumps.pump-5').context().get('swarmBestFitness', particleFitness) + var neighborsList = ['DoAB-PBR02-group','DoAB-PBR03-group','DoAB-PBR04-group','DoAB-PBR05-group'] // TODO add identifier from UserDefinedProtocol. !!! needs to be adjusted for each particle + var neighborsPosition = neighborsList.slice() + neighborsPosition.forEach(function(value, index, arr) { arr[index] = theServer.getGroupByName(value).context().get('particlePosition', undefined) * 2 }) + var neighborsFitness = neighborsList.slice() + neighborsFitness.forEach(function(value, index, arr) { arr[index] = theServer.getGroupByName(value).context().get('particleFitness', undefined) }) + var neighborsBestFitness = Math.min.apply(null, neighborsFitness) + var neighborsBestPosition = neighborsPosition[neighborsFitness.indexOf(neighborsBestFitness)] + var newPosition = [] + // try { + // swarmBestPosition = swarmBestPosition.split(',') + // } catch (error) { + // debugLogger('BioArInEO-PSO ERROR. ' + error.name + ' : ' + error.message) + // } + // for i in range(len(Swarm.multiparametric_space)): + var particleCognitionLearning = 2.1 + var particleSocialLearning = 2.1 + var particleGlobalLearning = 1.1 + var particleInertiaWeighting = 2 * 0.9 / (particleCognitionLearning + particleSocialLearning + particleGlobalLearning - 2) + var newStep = particleInertiaWeighting * particleStep + particleCognitionLearning * Math.random() * (particleBestPosition - particlePosition) + particleSocialLearning * Math.random() * (neighborsBestPosition - particlePosition) + particleGlobalLearning * Math.random() * (swarmBestPosition - particlePosition) + theAccessory.context().put('particleStep', newStep) + if (!(particleFitness > swarmBestFitness)) { + theAccessory.context().put('particleBestFitness', particleFitness) + swarmLeader.getAccessory('pumps.pump-5').context().put('swarmBestFitness', particleFitness) + swarmLeader.getAccessory('pumps.pump-5').context().put('swarmBestParticle', theGroup) + } else if (!(particleFitness > particleBestFitness)) { + theAccessory.context().put('particleBestFitness', particleFitness) + } + newPosition.push(particlePosition + newStep) + theAccessory.context().put('particlePosition', newPosition) + debugLogger('BioArInEO-PSO new step is ' + newStep + ' and position is ' + newPosition) + return newPosition +} From 7f8f0a9f74fe133f72cf0d43ce3585761c2e0bb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cerven=C3=BD?= Date: Mon, 17 Aug 2020 08:57:53 +0200 Subject: [PATCH 02/47] Parameter space limit handling added --- PP-GrowthOptimizer.js | 20 ++++++++++++--- jsDoc.js | 57 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 4 deletions(-) create mode 100644 jsDoc.js diff --git a/PP-GrowthOptimizer.js b/PP-GrowthOptimizer.js index 4c7cc15..2c2a950 100644 --- a/PP-GrowthOptimizer.js +++ b/PP-GrowthOptimizer.js @@ -39,8 +39,8 @@ var UserDefinedProtocol = { * @author CzechGlobe - Department of Adaptive Biotechnologies (JaCe) * @copyright Jan Červený 2020(c) * @license MIT - * @version 3.3.0 - * @modified 16.8.2020 (JaCe) + * @version 3.3.1 + * @modified 17.8.2020 (JaCe) * * @notes For proper functionality of the script "OD Regulator" protocol has to be disabled as well as chosen * controlled accessory protocols (i.e. Lights, Thermoregulation, GMS, Stirrer). @@ -466,6 +466,8 @@ function PSO (particleFitness) { * * Changes parameter(s) in optimal way as evaluated by PSO algorithm. It requires actual fitness of the particle that is expected to be minimized, e.g. doubling time. * + * @since 3.3.0 + * * @param {number} particleFitness Fitness of the particle. * * @return {array} New parameters / conditions. @@ -475,7 +477,8 @@ function PSO (particleFitness) { var particlePosition = theAccessory.context().get('particlePosition', UserDefinedProtocol.controlledParameterSteps[0]) var particleBestPosition = theAccessory.context().get('particleBestPosition', particlePosition) var particleBestFitness = theAccessory.context().get('particleBestFitness', particleFitness) - var particleStep = theAccessory.context().get('particleStep', 0.2 * parameterSearchRange[1] - parameterSearchRange[0]) // TODO change to multiparameter + var particleStep = theAccessory.context().get('particleStep', 0.2 * parameterSearchRange[1] - parameterSearchRange[0]) // TODO change to multiparameter version + var maxStep = 5 // TODO adjust to multiparameter version, i.e. for each parameter reccommended limit (5 is for temperature) var swarmLeader = theServer.getGroupByName('DoAB-PBR01-group') // TODO add identifier from UserDefinedProtocol instead var swarmBestPosition = swarmLeader.getAccessory('pumps.pump-5').context().get('swarmBestPosition', particlePosition) var swarmBestFitness = swarmLeader.getAccessory('pumps.pump-5').context().get('swarmBestFitness', particleFitness) @@ -498,6 +501,9 @@ function PSO (particleFitness) { var particleGlobalLearning = 1.1 var particleInertiaWeighting = 2 * 0.9 / (particleCognitionLearning + particleSocialLearning + particleGlobalLearning - 2) var newStep = particleInertiaWeighting * particleStep + particleCognitionLearning * Math.random() * (particleBestPosition - particlePosition) + particleSocialLearning * Math.random() * (neighborsBestPosition - particlePosition) + particleGlobalLearning * Math.random() * (swarmBestPosition - particlePosition) + if (newStep > maxStep) { + newStep = maxStep + } theAccessory.context().put('particleStep', newStep) if (!(particleFitness > swarmBestFitness)) { theAccessory.context().put('particleBestFitness', particleFitness) @@ -506,7 +512,13 @@ function PSO (particleFitness) { } else if (!(particleFitness > particleBestFitness)) { theAccessory.context().put('particleBestFitness', particleFitness) } - newPosition.push(particlePosition + newStep) + if ((particlePosition + newStep) > parameterSearchRange[1]) { + newPosition.push(parameterSearchRange[1]) + } else if ((particlePosition + newStep) < parameterSearchRange[0]) { + newPosition.push(parameterSearchRange[0]) + } else { + newPosition.push(particlePosition + newStep) + } theAccessory.context().put('particlePosition', newPosition) debugLogger('BioArInEO-PSO new step is ' + newStep + ' and position is ' + newPosition) return newPosition diff --git a/jsDoc.js b/jsDoc.js new file mode 100644 index 0000000..2bbed38 --- /dev/null +++ b/jsDoc.js @@ -0,0 +1,57 @@ +Functions should be formatted as follows: + +Summary: A brief, one line explanation of the purpose of the function. Use a period at the end. +Description: A supplement to the summary, providing a more detailed description. Use a period at the end. +@deprecated x.x.x: Only use for deprecated functions, and provide the version the function was deprecated which should always be 3-digit (e.g. @since 3.6.0), and the function to use instead. +@since x.x.x: Should be 3-digit for initial introduction (e.g. @since 3.6.0). If significant changes are made, additional @since tags, versions, and descriptions should be added to serve as a changelog. +@access: Only use for functions if private. If the function is private, it is intended for internal use only, and there will be no documentation for it in the code reference. +@class: Use for class constructors. +@augments: For class constuctors, list direct parents. +@mixes: List mixins that are mixed into the object. +@alias: If this function is first assigned to a temporary variable this allows you to change the name it’s documented under. +@memberof: Namespace that this function is contained within if JSDoc is unable to resolve this automatically. +@static: For classes, used to mark that a function is a static method on the class constructor. +@see: A function or class relied on. +@link: URL that provides more information. +@fires: Event fired by the function. Events tied to a specific class should list the class name. +@listens: Events this function listens for. An event must be prefixed with the event namespace. Events tied to a specific class should list the class name. +@global: Marks this function as a global function to be included in the global namespace. +@param: Give a brief description of the variable; denote particulars (e.g. if the variable is optional, its default) with JSDoc @param syntax. Use a period at the end. +@yield: For generator functions, a description of the values expected to be yielded from this function. As with other descriptions, include a period at the end. +@return: Note the period after the description. + +/** + * Summary. (use period) + * + * Description. (use period) + * + * @since x.x.x + * @deprecated x.x.x Use new_function_name() instead. + * @access private + * + * @class + * @augments parent + * @mixes mixin + * + * @alias realName + * @memberof namespace + * + * @see Function/class relied on + * @link URL + * @global + * + * @fires eventName + * @fires className#eventName + * @listens event:eventName + * @listens className~event:eventName + * + * @param {type} var Description. + * @param {type} [var] Description of optional variable. + * @param {type} [var=default] Description of optional variable with default variable. + * @param {Object} objectVar Description. + * @param {type} objectVar.key Description of a key in the objectVar parameter. + * + * @yield {type} Yielded value description. + * + * @return {type} Return value description. + */ From 80caf625bd210ca3008822ef69d69f9b56f6014c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cerven=C3=BD?= Date: Wed, 19 Aug 2020 10:56:35 +0200 Subject: [PATCH 03/47] Bufixes, code optimization and sendMail implementation --- PP-GrowthOptimizer.js | 70 +++++++++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 26 deletions(-) diff --git a/PP-GrowthOptimizer.js b/PP-GrowthOptimizer.js index 2c2a950..4bc7e3a 100644 --- a/PP-GrowthOptimizer.js +++ b/PP-GrowthOptimizer.js @@ -5,8 +5,8 @@ var UserDefinedProtocol = { turbidostatODType: 720, ODReadoutInterval: 60, // -optimizer parameters - controlledParameter: 'none', - controlledParameterSteps: [[ 1100, 25 ], [ 440, 25 ], [ 55, 25 ]], + controlledParameter: 'temperature', + controlledParameterSteps: [ 25 ], particleSwarmOptimizer: true, // -optimizer stability check growthStatistics: true, @@ -39,8 +39,8 @@ var UserDefinedProtocol = { * @author CzechGlobe - Department of Adaptive Biotechnologies (JaCe) * @copyright Jan Červený 2020(c) * @license MIT - * @version 3.3.1 - * @modified 17.8.2020 (JaCe) + * @version 3.3.2 + * @modified 19.8.2020 (JaCe) * * @notes For proper functionality of the script "OD Regulator" protocol has to be disabled as well as chosen * controlled accessory protocols (i.e. Lights, Thermoregulation, GMS, Stirrer). @@ -183,7 +183,7 @@ try { result = controlPump() } } catch (error) { - debugLogger('O2 evol./resp. activity check ERROR. ' + error.name + ' : ' + error.message) + debugLogger('The pump activity ERROR. ' + error.name + ' : ' + error.message) } /** function setODSensorString (ODType) { @@ -244,13 +244,13 @@ function controlParameter (parameter, values) { unit = ' uE' light0.setRunningProtoConfig(new ProtoConfig(Number(values[0]))) // Red light1.setRunningProtoConfig(new ProtoConfig(Number(values[1]))) // Blue || White - debugLogger('Lights changed.') + debugLogger('Lights changed. Channel 0 set to ' + round(values[0], 0) + unit + ' and channel 1 set to ' + round(values[1], 0) + unit) break case 'temperature': var thermoreg = theGroup.getAccessory('thermo.thermo-reg') - unit = String.fromCharCode(176) + 'C' + unit = ' ' + String.fromCharCode(176) + 'C' thermoreg.setRunningProtoConfig(new ProtoConfig(Number(values))) - debugLogger('Temperature changed.') + debugLogger('Temperature changed. Thermoregulator set to ' + round(values, 2) + unit) break case 'GMS': var valve0 = UserDefinedProtocol.groupGMS.getAccessory('gas-mixer.valve-0-reg') // CO2 @@ -260,19 +260,19 @@ function controlParameter (parameter, values) { valve1.setRunningProtoConfig(new ProtoConfig(Number(values[1]))) var flowAir = valve0.getProtoConfigValue() var flowCO2 = valve1.getProtoConfigValue() - debugLogger('GMS settings changed. Gas Mixing set to Air flow ' + round(flowAir, 2) + ' ml/min and CO2 flow ' + round(flowCO2, 2) + ' ml/min (' + round((flowCO2 / (flowCO2 + flowAir) + 400 / 1e6) * 100, 1) + '%)') + debugLogger('GMS settings changed. Gas Mixing set to Air flow ' + round(flowAir, 0) + unit + ' and CO2 flow ' + round(flowCO2, 1) + unit + ' (' + round((flowCO2 / (flowCO2 + flowAir) + 395 / 1e6) * 100, 1) + '%)') break case 'stirrer': var stirrer = theGroup.getAccessory('pwm.stirrer') unit = '%' stirrer.setRunningProtoConfig(new ProtoConfig(Number(values))) - debugLogger('Stirrer changed.') + debugLogger('Stirrer changed. Stirrer set to ' + round(values, 0) + unit) break case 'ODRange': theAccessory.context().put('odMinModifier', Number(values[0]) / UserDefinedProtocol.turbidostatODMin) theAccessory.context().put('odMaxModifier', Number(values[1]) / UserDefinedProtocol.turbidostatODMax) unit = ' AU' - debugLogger('Turbidostat OD range changed.') + debugLogger('Turbidostat settings changed. OD range set to ' + round(values[0], 3) + ' - ' + round(values[1], 3) + unit) break default: return null @@ -337,7 +337,14 @@ function controlPump () { } if (theAccessory.context().getInt('stabilizedTimeMax', 0) <= Number(theExperiment.getDurationSec()) && (stepCounter !== 0)) { theAccessory.context().put('stabilizedTimeMax', theExperiment.getDurationSec() + UserDefinedProtocol.stabilizationTimeMax * 3600) - if (UserDefinedProtocol.controlledParameterSteps.length > 1) { + if (UserDefinedProtocol.particleSwarmOptimizer) { + var stepDoublingTime = theAccessory.context().get('stepDoublingTime', [ 999.9 ]) + controlParameter(UserDefinedProtocol.controlledParameter, PSO(stepDoublingTime[stepDoublingTime.length - 1])) //TODO change to average of last n steps + theAccessory.context().remove('stepCounter') + theAccessory.context().remove('expDuration') + theAccessory.context().remove('stepDoublingTime') + theAccessory.context().remove('stabilizedTime') + } else if (UserDefinedProtocol.controlledParameterSteps.length > 1) { if (changeCounter < (UserDefinedProtocol.controlledParameterSteps.length - 1)) { controlParameter(UserDefinedProtocol.controlledParameter, UserDefinedProtocol.controlledParameterSteps[++changeCounter]) theAccessory.context().put('changeCounter', changeCounter) @@ -352,7 +359,7 @@ function controlPump () { theAccessory.context().remove('expDuration') theAccessory.context().remove('stepDoublingTime') theAccessory.context().remove('stabilizedTime') - } + } } // Start step growth rate evaluation if (((odValue > (UserDefinedProtocol.turbidostatODMax * odMaxModifier)) && !pumpState)) { @@ -361,7 +368,7 @@ function controlPump () { // var stepCounter = theAccessory.context().getInt('stepCounter', 0) var expDuration = theAccessory.context().get('expDuration', 0.0) var stepDuration = theAccessory.context().get('stepDuration', 0.0) - var stepDoublingTime = theAccessory.context().get('stepDoublingTime', 0.0) + var stepDoublingTime = theAccessory.context().get('stepDoublingTime', [ 999.9 ]) var stabilizedTime = theAccessory.context().getInt('stabilizedTime', 0) // var stabilizedTimeMax = theAccessory.context().getInt('stabilizedTimeMax', 0) if (!Array.isArray(expDuration)) { @@ -422,7 +429,7 @@ function controlPump () { if (((stepDoublingTimeIC95 / stepDoublingTimeAvg) <= (UserDefinedProtocol.intervalOfConfidenceMax / 100) && (Math.abs(stepTrend / stepDoublingTimeAvg) <= (UserDefinedProtocol.growthTrendMax / 100)) && (stabilizedTime <= Number(theExperiment.getDurationSec())))) { theAccessory.context().put('modeStabilized', 1) // changeCounter = theAccessory.context().getInt('changeCounter', 0) - theExperiment.addEvent('*** Stabilized doubling time Dt (' + theGroup.getAccessory('thermo.thermo-reg').getValue() + String.fromCharCode(176) + 'C, ' + theAccessory.context().getString('controlledParameterText', 'no parameter') + ') is ' + round(stepDoublingTimeAvg, 2) + String.fromCharCode(177) + round(stepDoublingTimeIC95, 2) + ' h (IC95)') + theExperiment.addEvent('*** Stabilized doubling time Dt (' + theGroup.getAccessory('thermo.thermo-reg').getValue() + ' ' + String.fromCharCode(176) + 'C, ' + theAccessory.context().getString('controlledParameterText', 'no parameter') + ') is ' + round(stepDoublingTimeAvg, 2) + String.fromCharCode(177) + round(stepDoublingTimeIC95, 2) + ' h (IC95)') if (UserDefinedProtocol.particleSwarmOptimizer) { controlParameter(UserDefinedProtocol.controlledParameter, PSO(stepDoublingTimeAvg)) } else if (UserDefinedProtocol.controlledParameterSteps.length > 1) { @@ -472,23 +479,30 @@ function PSO (particleFitness) { * * @return {array} New parameters / conditions. */ + debugLogger('BioArInEO-PSO executed') theAccessory.context().put('particleFitness', particleFitness) var parameterSearchRange = [15, 35] - var particlePosition = theAccessory.context().get('particlePosition', UserDefinedProtocol.controlledParameterSteps[0]) - var particleBestPosition = theAccessory.context().get('particleBestPosition', particlePosition) + var particlePosition = Number(theAccessory.context().get('particlePosition', UserDefinedProtocol.controlledParameterSteps[0])) //TODO multidimensional support + theAccessory.context().put('particlePosition', particlePosition) + var particleBestPosition = Number(theAccessory.context().get('particleBestPosition', particlePosition)) var particleBestFitness = theAccessory.context().get('particleBestFitness', particleFitness) var particleStep = theAccessory.context().get('particleStep', 0.2 * parameterSearchRange[1] - parameterSearchRange[0]) // TODO change to multiparameter version var maxStep = 5 // TODO adjust to multiparameter version, i.e. for each parameter reccommended limit (5 is for temperature) var swarmLeader = theServer.getGroupByName('DoAB-PBR01-group') // TODO add identifier from UserDefinedProtocol instead - var swarmBestPosition = swarmLeader.getAccessory('pumps.pump-5').context().get('swarmBestPosition', particlePosition) + var swarmBestPosition = Number(swarmLeader.getAccessory('pumps.pump-5').context().get('swarmBestPosition', particlePosition)) var swarmBestFitness = swarmLeader.getAccessory('pumps.pump-5').context().get('swarmBestFitness', particleFitness) - var neighborsList = ['DoAB-PBR02-group','DoAB-PBR03-group','DoAB-PBR04-group','DoAB-PBR05-group'] // TODO add identifier from UserDefinedProtocol. !!! needs to be adjusted for each particle - var neighborsPosition = neighborsList.slice() - neighborsPosition.forEach(function(value, index, arr) { arr[index] = theServer.getGroupByName(value).context().get('particlePosition', undefined) * 2 }) - var neighborsFitness = neighborsList.slice() - neighborsFitness.forEach(function(value, index, arr) { arr[index] = theServer.getGroupByName(value).context().get('particleFitness', undefined) }) + debugLogger('BioArInEO-PSO best swarm position is ' + swarmBestPosition + ' with fitness ' + swarmBestFitness) + var neighborsList = ['DoAB-PBR02-group'] // TODO add identifier from UserDefinedProtocol. !!! needs to be adjusted for each particle + var neighborsPosition = [] + var neighborsFitness = [] + var index, len + for ( index = 0, len = neighborsList.length; index < len; ++index) { + neighborsPosition.push(theServer.getGroupByName(neighborsList[index]).getAccessory('pumps.pump-5').context().get('particlePosition', particlePosition)); + neighborsFitness.push(theServer.getGroupByName(neighborsList[index]).getAccessory('pumps.pump-5').context().get('particleFitness', particleFitness)) + } var neighborsBestFitness = Math.min.apply(null, neighborsFitness) - var neighborsBestPosition = neighborsPosition[neighborsFitness.indexOf(neighborsBestFitness)] + var neighborsBestPosition = Number(neighborsPosition[neighborsFitness.indexOf(neighborsBestFitness)]) + debugLogger('BioArInEO-PSO best neighbors position is ' + neighborsBestPosition + ' with fitness ' + neighborsBestFitness) var newPosition = [] // try { // swarmBestPosition = swarmBestPosition.split(',') @@ -499,17 +513,21 @@ function PSO (particleFitness) { var particleCognitionLearning = 2.1 var particleSocialLearning = 2.1 var particleGlobalLearning = 1.1 - var particleInertiaWeighting = 2 * 0.9 / (particleCognitionLearning + particleSocialLearning + particleGlobalLearning - 2) + var particleInertiaWeighting = 2 * 0.8 / (particleCognitionLearning + particleSocialLearning + particleGlobalLearning - 2) + debugLogger('BioArInEO-PSO evaluating new step') var newStep = particleInertiaWeighting * particleStep + particleCognitionLearning * Math.random() * (particleBestPosition - particlePosition) + particleSocialLearning * Math.random() * (neighborsBestPosition - particlePosition) + particleGlobalLearning * Math.random() * (swarmBestPosition - particlePosition) if (newStep > maxStep) { newStep = maxStep } theAccessory.context().put('particleStep', newStep) if (!(particleFitness > swarmBestFitness)) { + theAccessory.context().put('particleBestPosition', particlePosition) theAccessory.context().put('particleBestFitness', particleFitness) + swarmLeader.getAccessory('pumps.pump-5').context().put('swarmBestPosition', particlePosition) swarmLeader.getAccessory('pumps.pump-5').context().put('swarmBestFitness', particleFitness) swarmLeader.getAccessory('pumps.pump-5').context().put('swarmBestParticle', theGroup) } else if (!(particleFitness > particleBestFitness)) { + theAccessory.context().put('particleBestPosition', particlePosition) theAccessory.context().put('particleBestFitness', particleFitness) } if ((particlePosition + newStep) > parameterSearchRange[1]) { @@ -519,7 +537,7 @@ function PSO (particleFitness) { } else { newPosition.push(particlePosition + newStep) } - theAccessory.context().put('particlePosition', newPosition) debugLogger('BioArInEO-PSO new step is ' + newStep + ' and position is ' + newPosition) + theServer.sendMail('PSO on ' + theGroup.getName() , 'INFO', ': new step is ' + newStep + ' and position is ' + newPosition) // Email notifications return newPosition } From 61999d9e0768dd4e88a2b101caaf1cd1bf657349 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cerven=C3=BD?= Date: Wed, 19 Aug 2020 11:05:35 +0200 Subject: [PATCH 04/47] PSO particle initial step direction randomized --- PP-GrowthOptimizer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PP-GrowthOptimizer.js b/PP-GrowthOptimizer.js index 4bc7e3a..82c24c5 100644 --- a/PP-GrowthOptimizer.js +++ b/PP-GrowthOptimizer.js @@ -486,7 +486,7 @@ function PSO (particleFitness) { theAccessory.context().put('particlePosition', particlePosition) var particleBestPosition = Number(theAccessory.context().get('particleBestPosition', particlePosition)) var particleBestFitness = theAccessory.context().get('particleBestFitness', particleFitness) - var particleStep = theAccessory.context().get('particleStep', 0.2 * parameterSearchRange[1] - parameterSearchRange[0]) // TODO change to multiparameter version + var particleStep = theAccessory.context().get('particleStep', 0.2 * (parameterSearchRange[1] - parameterSearchRange[0]) * Math.sign(getRandomOnInterval(-1, 1))) // TODO change to multiparameter version var maxStep = 5 // TODO adjust to multiparameter version, i.e. for each parameter reccommended limit (5 is for temperature) var swarmLeader = theServer.getGroupByName('DoAB-PBR01-group') // TODO add identifier from UserDefinedProtocol instead var swarmBestPosition = Number(swarmLeader.getAccessory('pumps.pump-5').context().get('swarmBestPosition', particlePosition)) From 0fe05837e6293064ae5cc7428e117e8aca2aa2c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cerven=C3=BD?= Date: Wed, 19 Aug 2020 18:38:54 +0200 Subject: [PATCH 05/47] No support for Math.sign() fixed --- PP-GrowthOptimizer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PP-GrowthOptimizer.js b/PP-GrowthOptimizer.js index 82c24c5..a3d87f5 100644 --- a/PP-GrowthOptimizer.js +++ b/PP-GrowthOptimizer.js @@ -486,7 +486,7 @@ function PSO (particleFitness) { theAccessory.context().put('particlePosition', particlePosition) var particleBestPosition = Number(theAccessory.context().get('particleBestPosition', particlePosition)) var particleBestFitness = theAccessory.context().get('particleBestFitness', particleFitness) - var particleStep = theAccessory.context().get('particleStep', 0.2 * (parameterSearchRange[1] - parameterSearchRange[0]) * Math.sign(getRandomOnInterval(-1, 1))) // TODO change to multiparameter version + var particleStep = theAccessory.context().get('particleStep', 0.2 * (parameterSearchRange[1] - parameterSearchRange[0]) * (getRandomOnInterval(-1, 1) > 0 ? 1 : -1)) // ! PSI PBR JS doesn't support Math.sign() var maxStep = 5 // TODO adjust to multiparameter version, i.e. for each parameter reccommended limit (5 is for temperature) var swarmLeader = theServer.getGroupByName('DoAB-PBR01-group') // TODO add identifier from UserDefinedProtocol instead var swarmBestPosition = Number(swarmLeader.getAccessory('pumps.pump-5').context().get('swarmBestPosition', particlePosition)) From 07e796717c5e3caab82c9b496a81d35ba9c2fccb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cerven=C3=BD?= Date: Thu, 20 Aug 2020 14:24:59 +0200 Subject: [PATCH 06/47] PSO fitness averaging when stabilizationTimeMax --- PP-GrowthOptimizer.js | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/PP-GrowthOptimizer.js b/PP-GrowthOptimizer.js index a3d87f5..83a51ba 100644 --- a/PP-GrowthOptimizer.js +++ b/PP-GrowthOptimizer.js @@ -39,8 +39,8 @@ var UserDefinedProtocol = { * @author CzechGlobe - Department of Adaptive Biotechnologies (JaCe) * @copyright Jan Červený 2020(c) * @license MIT - * @version 3.3.2 - * @modified 19.8.2020 (JaCe) + * @version 3.3.3 + * @modified 20.8.2020 (JaCe) * * @notes For proper functionality of the script "OD Regulator" protocol has to be disabled as well as chosen * controlled accessory protocols (i.e. Lights, Thermoregulation, GMS, Stirrer). @@ -231,6 +231,9 @@ function debugLogger (message, status) { return null } } +function getSumArrReduce(total, num) { + return total + num; +} function controlParameter (parameter, values) { // Control accessory functions if ((parameter === undefined) || (parameter === 'none') || (values === undefined)) { @@ -339,7 +342,8 @@ function controlPump () { theAccessory.context().put('stabilizedTimeMax', theExperiment.getDurationSec() + UserDefinedProtocol.stabilizationTimeMax * 3600) if (UserDefinedProtocol.particleSwarmOptimizer) { var stepDoublingTime = theAccessory.context().get('stepDoublingTime', [ 999.9 ]) - controlParameter(UserDefinedProtocol.controlledParameter, PSO(stepDoublingTime[stepDoublingTime.length - 1])) //TODO change to average of last n steps + stepDoublingTime = stepDoublingTime.length > 2 ? stepDoublingTime.reduce(getSumArrReduce, 0) / stepDoublingTime.length : stepDoublingTime[stepDoublingTime.length - 1] + controlParameter(UserDefinedProtocol.controlledParameter, PSO(stepDoublingTime)) theAccessory.context().remove('stepCounter') theAccessory.context().remove('expDuration') theAccessory.context().remove('stepDoublingTime') @@ -480,10 +484,10 @@ function PSO (particleFitness) { * @return {array} New parameters / conditions. */ debugLogger('BioArInEO-PSO executed') - theAccessory.context().put('particleFitness', particleFitness) + theAccessory.context().put('particleLastFitness', particleFitness) var parameterSearchRange = [15, 35] var particlePosition = Number(theAccessory.context().get('particlePosition', UserDefinedProtocol.controlledParameterSteps[0])) //TODO multidimensional support - theAccessory.context().put('particlePosition', particlePosition) + theAccessory.context().put('particleLastPosition', particlePosition) var particleBestPosition = Number(theAccessory.context().get('particleBestPosition', particlePosition)) var particleBestFitness = theAccessory.context().get('particleBestFitness', particleFitness) var particleStep = theAccessory.context().get('particleStep', 0.2 * (parameterSearchRange[1] - parameterSearchRange[0]) * (getRandomOnInterval(-1, 1) > 0 ? 1 : -1)) // ! PSI PBR JS doesn't support Math.sign() @@ -497,8 +501,8 @@ function PSO (particleFitness) { var neighborsFitness = [] var index, len for ( index = 0, len = neighborsList.length; index < len; ++index) { - neighborsPosition.push(theServer.getGroupByName(neighborsList[index]).getAccessory('pumps.pump-5').context().get('particlePosition', particlePosition)); - neighborsFitness.push(theServer.getGroupByName(neighborsList[index]).getAccessory('pumps.pump-5').context().get('particleFitness', particleFitness)) + neighborsPosition.push(theServer.getGroupByName(neighborsList[index]).getAccessory('pumps.pump-5').context().get('particleLastPosition', particlePosition)); + neighborsFitness.push(theServer.getGroupByName(neighborsList[index]).getAccessory('pumps.pump-5').context().get('particleLastFitness', particleFitness)) } var neighborsBestFitness = Math.min.apply(null, neighborsFitness) var neighborsBestPosition = Number(neighborsPosition[neighborsFitness.indexOf(neighborsBestFitness)]) @@ -511,8 +515,8 @@ function PSO (particleFitness) { // } // for i in range(len(Swarm.multiparametric_space)): var particleCognitionLearning = 2.1 - var particleSocialLearning = 2.1 - var particleGlobalLearning = 1.1 + var particleSocialLearning = 1.1 + var particleGlobalLearning = 1.6 var particleInertiaWeighting = 2 * 0.8 / (particleCognitionLearning + particleSocialLearning + particleGlobalLearning - 2) debugLogger('BioArInEO-PSO evaluating new step') var newStep = particleInertiaWeighting * particleStep + particleCognitionLearning * Math.random() * (particleBestPosition - particlePosition) + particleSocialLearning * Math.random() * (neighborsBestPosition - particlePosition) + particleGlobalLearning * Math.random() * (swarmBestPosition - particlePosition) @@ -537,6 +541,7 @@ function PSO (particleFitness) { } else { newPosition.push(particlePosition + newStep) } + theAccessory.context().put('particlePosition', newPosition) debugLogger('BioArInEO-PSO new step is ' + newStep + ' and position is ' + newPosition) theServer.sendMail('PSO on ' + theGroup.getName() , 'INFO', ': new step is ' + newStep + ' and position is ' + newPosition) // Email notifications return newPosition From 3a80951ce5a9b4e9abb29f1f5e446c9919bb2c42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cerven=C3=BD?= Date: Thu, 20 Aug 2020 15:09:22 +0200 Subject: [PATCH 07/47] PSO parameters optimized + Dt averaging interval reduced --- PP-GrowthOptimizer.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/PP-GrowthOptimizer.js b/PP-GrowthOptimizer.js index 83a51ba..42520db 100644 --- a/PP-GrowthOptimizer.js +++ b/PP-GrowthOptimizer.js @@ -342,7 +342,8 @@ function controlPump () { theAccessory.context().put('stabilizedTimeMax', theExperiment.getDurationSec() + UserDefinedProtocol.stabilizationTimeMax * 3600) if (UserDefinedProtocol.particleSwarmOptimizer) { var stepDoublingTime = theAccessory.context().get('stepDoublingTime', [ 999.9 ]) - stepDoublingTime = stepDoublingTime.length > 2 ? stepDoublingTime.reduce(getSumArrReduce, 0) / stepDoublingTime.length : stepDoublingTime[stepDoublingTime.length - 1] + var len = stepDoublingTime.length + stepDoublingTime = len > 2 ? stepDoublingTime.slice(1, len).reduce(getSumArrReduce, 0) / (len - 1) : stepDoublingTime[len - 1] controlParameter(UserDefinedProtocol.controlledParameter, PSO(stepDoublingTime)) theAccessory.context().remove('stepCounter') theAccessory.context().remove('expDuration') From 4622bafe18910eb8f405b7353e5e9e0583dcd74d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cerven=C3=BD?= Date: Sun, 23 Aug 2020 22:27:31 +0200 Subject: [PATCH 08/47] Bugfixes --- PP-GrowthOptimizer.js | 53 +++++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/PP-GrowthOptimizer.js b/PP-GrowthOptimizer.js index 42520db..98e8a21 100644 --- a/PP-GrowthOptimizer.js +++ b/PP-GrowthOptimizer.js @@ -1,23 +1,23 @@ var UserDefinedProtocol = { // -turbidostat settings - turbidostatODMin: 0.4, - turbidostatODMax: 0.425, + turbidostatODMin: 0.18, + turbidostatODMax: 0.195, turbidostatODType: 720, ODReadoutInterval: 60, // -optimizer parameters controlledParameter: 'temperature', - controlledParameterSteps: [ 25 ], + controlledParameterSteps: [ 28 ], particleSwarmOptimizer: true, // -optimizer stability check growthStatistics: true, regressionODType: 680, - regressionCoDMin: 75, - stabilizationTimeMin: 12, - stabilizationTimeMax: 36, - growthRateEvalFrac: 2 / 3, + regressionCoDMin: 60, + stabilizationTimeMin: 3, + stabilizationTimeMax: 7, + growthRateEvalFrac: 3 / 4, analyzedSteps: 6, - growthTrendMax: 1.5, - intervalOfConfidenceMax: 3.5, + growthTrendMax: 15, + intervalOfConfidenceMax: 35, // -peristaltic pump settings peristalticPumpID: 5, peristalticPumpSpeed: 100, @@ -39,8 +39,8 @@ var UserDefinedProtocol = { * @author CzechGlobe - Department of Adaptive Biotechnologies (JaCe) * @copyright Jan Červený 2020(c) * @license MIT - * @version 3.3.3 - * @modified 20.8.2020 (JaCe) + * @version 3.3.4 + * @modified 23.8.2020 (JaCe) * * @notes For proper functionality of the script "OD Regulator" protocol has to be disabled as well as chosen * controlled accessory protocols (i.e. Lights, Thermoregulation, GMS, Stirrer). @@ -339,7 +339,7 @@ function controlPump () { debugLogger('OD range reversed.', 0) } if (theAccessory.context().getInt('stabilizedTimeMax', 0) <= Number(theExperiment.getDurationSec()) && (stepCounter !== 0)) { - theAccessory.context().put('stabilizedTimeMax', theExperiment.getDurationSec() + UserDefinedProtocol.stabilizationTimeMax * 3600) + theAccessory.context().put('stabilizedTimeMax', theExperiment.getDurationSec() + UserDefinedProtocol.stabilizationTimeMax * 3600) // TODO allocate var for getDurationSec if (UserDefinedProtocol.particleSwarmOptimizer) { var stepDoublingTime = theAccessory.context().get('stepDoublingTime', [ 999.9 ]) var len = stepDoublingTime.length @@ -448,12 +448,12 @@ function controlPump () { controlParameter(UserDefinedProtocol.controlledParameter, UserDefinedProtocol.controlledParameterSteps[1]) theAccessory.context().put('changeCounter', 1) } - theAccessory.context().put('stabilizedTimeMax', theExperiment.getDurationSec() + UserDefinedProtocol.stabilizationTimeMax * 3600) - theAccessory.context().remove('stepCounter') - theAccessory.context().remove('expDuration') - theAccessory.context().remove('stepDoublingTime') - theAccessory.context().remove('stabilizedTime') } + theAccessory.context().put('stabilizedTimeMax', theExperiment.getDurationSec() + UserDefinedProtocol.stabilizationTimeMax * 3600) + theAccessory.context().remove('stepCounter') + theAccessory.context().remove('expDuration') + theAccessory.context().remove('stepDoublingTime') + theAccessory.context().remove('stabilizedTime') } } } @@ -484,15 +484,18 @@ function PSO (particleFitness) { * * @return {array} New parameters / conditions. */ + if (particleFitness === undefined) { + particleFitness = [ 999.9 ] + } debugLogger('BioArInEO-PSO executed') theAccessory.context().put('particleLastFitness', particleFitness) - var parameterSearchRange = [15, 35] + var parameterSearchRange = [ 18, 38 ] var particlePosition = Number(theAccessory.context().get('particlePosition', UserDefinedProtocol.controlledParameterSteps[0])) //TODO multidimensional support - theAccessory.context().put('particleLastPosition', particlePosition) - var particleBestPosition = Number(theAccessory.context().get('particleBestPosition', particlePosition)) + theAccessory.context().put('particleLastPosition', particlePosition + var particleBestPosition = Number(theAccessory.context().get('particleBestPosition', particlePosition) var particleBestFitness = theAccessory.context().get('particleBestFitness', particleFitness) var particleStep = theAccessory.context().get('particleStep', 0.2 * (parameterSearchRange[1] - parameterSearchRange[0]) * (getRandomOnInterval(-1, 1) > 0 ? 1 : -1)) // ! PSI PBR JS doesn't support Math.sign() - var maxStep = 5 // TODO adjust to multiparameter version, i.e. for each parameter reccommended limit (5 is for temperature) + var maxStep = 150 // TODO adjust to multiparameter version, i.e. for each parameter reccommended limit (5 is for temperature) var swarmLeader = theServer.getGroupByName('DoAB-PBR01-group') // TODO add identifier from UserDefinedProtocol instead var swarmBestPosition = Number(swarmLeader.getAccessory('pumps.pump-5').context().get('swarmBestPosition', particlePosition)) var swarmBestFitness = swarmLeader.getAccessory('pumps.pump-5').context().get('swarmBestFitness', particleFitness) @@ -521,9 +524,9 @@ function PSO (particleFitness) { var particleInertiaWeighting = 2 * 0.8 / (particleCognitionLearning + particleSocialLearning + particleGlobalLearning - 2) debugLogger('BioArInEO-PSO evaluating new step') var newStep = particleInertiaWeighting * particleStep + particleCognitionLearning * Math.random() * (particleBestPosition - particlePosition) + particleSocialLearning * Math.random() * (neighborsBestPosition - particlePosition) + particleGlobalLearning * Math.random() * (swarmBestPosition - particlePosition) - if (newStep > maxStep) { - newStep = maxStep - } + if (Math.abs(newStep) > maxStep) { + newStep = maxStep * (newStep > 0 ? 1 : -1) + } theAccessory.context().put('particleStep', newStep) if (!(particleFitness > swarmBestFitness)) { theAccessory.context().put('particleBestPosition', particlePosition) @@ -544,6 +547,6 @@ function PSO (particleFitness) { } theAccessory.context().put('particlePosition', newPosition) debugLogger('BioArInEO-PSO new step is ' + newStep + ' and position is ' + newPosition) - theServer.sendMail('PSO on ' + theGroup.getName() , 'INFO', ': new step is ' + newStep + ' and position is ' + newPosition) // Email notifications + theServer.sendMail('PSO on ' + theGroup.getName() , 'NONE', ': new step is ' + newStep + ' and position is ' + newPosition) // Email notifications return newPosition } From cab280f44a961c1bbaedeef2a504184731e40533 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cerven=C3=BD?= Date: Mon, 24 Aug 2020 23:32:56 +0200 Subject: [PATCH 09/47] Multiparametric PSO implemented --- PP-GrowthOptimizer.js | 132 +++++++++++++++++++++++++++--------------- 1 file changed, 86 insertions(+), 46 deletions(-) diff --git a/PP-GrowthOptimizer.js b/PP-GrowthOptimizer.js index 98e8a21..9d93dd1 100644 --- a/PP-GrowthOptimizer.js +++ b/PP-GrowthOptimizer.js @@ -8,6 +8,12 @@ var UserDefinedProtocol = { controlledParameter: 'temperature', controlledParameterSteps: [ 28 ], particleSwarmOptimizer: true, + controlledParameters: [ 'temperature' ], + controlledParametersIC: [ 28 ], + parametersSearchRange: [ 18, 38 ], + parametersMaxStep: [ 5 ], + swarmLeaderGroup: 'DoAB-PBR01-group', + particleNeighborsGroupList: [ 'DoAB-PBR02-group' ], // -optimizer stability check growthStatistics: true, regressionODType: 680, @@ -21,7 +27,7 @@ var UserDefinedProtocol = { // -peristaltic pump settings peristalticPumpID: 5, peristalticPumpSpeed: 100, - peristalticPumpSlowDownRange: 25, + peristalticPumpSlowDownRange: 20, peristalticPumpSlowDownFactor: 75, // -advanced options growthRateEvalDelay: 420, @@ -39,8 +45,8 @@ var UserDefinedProtocol = { * @author CzechGlobe - Department of Adaptive Biotechnologies (JaCe) * @copyright Jan Červený 2020(c) * @license MIT - * @version 3.3.4 - * @modified 23.8.2020 (JaCe) + * @version 3.4.1 + * @modified 24.8.2020 (JaCe) * * @notes For proper functionality of the script "OD Regulator" protocol has to be disabled as well as chosen * controlled accessory protocols (i.e. Lights, Thermoregulation, GMS, Stirrer). @@ -249,6 +255,19 @@ function controlParameter (parameter, values) { light1.setRunningProtoConfig(new ProtoConfig(Number(values[1]))) // Blue || White debugLogger('Lights changed. Channel 0 set to ' + round(values[0], 0) + unit + ' and channel 1 set to ' + round(values[1], 0) + unit) break + case 'light-red': + var light0 = theGroup.getAccessory('actinic-lights.light-Red') + unit = ' uE' + light0.setRunningProtoConfig(new ProtoConfig(Number(values))) // Red + debugLogger('Red Light changed. Light set to ' + round(values, 0) + unit) + break + case 'light-white': + case 'light-blue': + var light1 = theGroup.getAccessory(theAccessory.context().get('light1String', 'actinic-lights.light-Blue')) + unit = ' uE' + light1.setRunningProtoConfig(new ProtoConfig(Number(values))) // Blue || White + debugLogger('White/blue light changed. Light set to ' + round(values, 0) + unit) + break case 'temperature': var thermoreg = theGroup.getAccessory('thermo.thermo-reg') unit = ' ' + String.fromCharCode(176) + 'C' @@ -344,7 +363,7 @@ function controlPump () { var stepDoublingTime = theAccessory.context().get('stepDoublingTime', [ 999.9 ]) var len = stepDoublingTime.length stepDoublingTime = len > 2 ? stepDoublingTime.slice(1, len).reduce(getSumArrReduce, 0) / (len - 1) : stepDoublingTime[len - 1] - controlParameter(UserDefinedProtocol.controlledParameter, PSO(stepDoublingTime)) + PSO(stepDoublingTime) theAccessory.context().remove('stepCounter') theAccessory.context().remove('expDuration') theAccessory.context().remove('stepDoublingTime') @@ -436,7 +455,7 @@ function controlPump () { // changeCounter = theAccessory.context().getInt('changeCounter', 0) theExperiment.addEvent('*** Stabilized doubling time Dt (' + theGroup.getAccessory('thermo.thermo-reg').getValue() + ' ' + String.fromCharCode(176) + 'C, ' + theAccessory.context().getString('controlledParameterText', 'no parameter') + ') is ' + round(stepDoublingTimeAvg, 2) + String.fromCharCode(177) + round(stepDoublingTimeIC95, 2) + ' h (IC95)') if (UserDefinedProtocol.particleSwarmOptimizer) { - controlParameter(UserDefinedProtocol.controlledParameter, PSO(stepDoublingTimeAvg)) + PSO(stepDoublingTimeAvg) } else if (UserDefinedProtocol.controlledParameterSteps.length > 1) { if (changeCounter < (UserDefinedProtocol.controlledParameterSteps.length - 1)) { controlParameter(UserDefinedProtocol.controlledParameter, UserDefinedProtocol.controlledParameterSteps[++changeCounter]) @@ -487,47 +506,73 @@ function PSO (particleFitness) { if (particleFitness === undefined) { particleFitness = [ 999.9 ] } - debugLogger('BioArInEO-PSO executed') theAccessory.context().put('particleLastFitness', particleFitness) - var parameterSearchRange = [ 18, 38 ] - var particlePosition = Number(theAccessory.context().get('particlePosition', UserDefinedProtocol.controlledParameterSteps[0])) //TODO multidimensional support - theAccessory.context().put('particleLastPosition', particlePosition - var particleBestPosition = Number(theAccessory.context().get('particleBestPosition', particlePosition) + var particlePosition = theAccessory.context().get('particlePosition', UserDefinedProtocol.controlledParametersIC) + debugLogger('BioArInEO-PSO executed with fitness ' + particleFitness + ' and position ' + particlePosition) + theAccessory.context().put('particleLastPosition', particlePosition) + var particleBestPosition = theAccessory.context().get('particleBestPosition', particlePosition) var particleBestFitness = theAccessory.context().get('particleBestFitness', particleFitness) - var particleStep = theAccessory.context().get('particleStep', 0.2 * (parameterSearchRange[1] - parameterSearchRange[0]) * (getRandomOnInterval(-1, 1) > 0 ? 1 : -1)) // ! PSI PBR JS doesn't support Math.sign() - var maxStep = 150 // TODO adjust to multiparameter version, i.e. for each parameter reccommended limit (5 is for temperature) - var swarmLeader = theServer.getGroupByName('DoAB-PBR01-group') // TODO add identifier from UserDefinedProtocol instead - var swarmBestPosition = Number(swarmLeader.getAccessory('pumps.pump-5').context().get('swarmBestPosition', particlePosition)) + var swarmLeader = theServer.getGroupByName(UserDefinedProtocol.swarmLeaderGroup) + var swarmBestPosition = swarmLeader.getAccessory('pumps.pump-5').context().get('swarmBestPosition', particlePosition) var swarmBestFitness = swarmLeader.getAccessory('pumps.pump-5').context().get('swarmBestFitness', particleFitness) - debugLogger('BioArInEO-PSO best swarm position is ' + swarmBestPosition + ' with fitness ' + swarmBestFitness) - var neighborsList = ['DoAB-PBR02-group'] // TODO add identifier from UserDefinedProtocol. !!! needs to be adjusted for each particle - var neighborsPosition = [] - var neighborsFitness = [] - var index, len - for ( index = 0, len = neighborsList.length; index < len; ++index) { - neighborsPosition.push(theServer.getGroupByName(neighborsList[index]).getAccessory('pumps.pump-5').context().get('particleLastPosition', particlePosition)); - neighborsFitness.push(theServer.getGroupByName(neighborsList[index]).getAccessory('pumps.pump-5').context().get('particleLastFitness', particleFitness)) + var neighborsList = UserDefinedProtocol.particleNeighborsGroupList + var parametersSearchRange = UserDefinedProtocol.parametersSearchRange + var particleStep = theAccessory.context().get('particleStep', undefined) + var temporaryTest = false + if (particleStep === undefined) { + temporaryTest = true + particleStep = [] } - var neighborsBestFitness = Math.min.apply(null, neighborsFitness) - var neighborsBestPosition = Number(neighborsPosition[neighborsFitness.indexOf(neighborsBestFitness)]) - debugLogger('BioArInEO-PSO best neighbors position is ' + neighborsBestPosition + ' with fitness ' + neighborsBestFitness) + var maxStep = [] + var neighborsBestPosition = [] + var neighborsBestFitness = [] var newPosition = [] - // try { - // swarmBestPosition = swarmBestPosition.split(',') - // } catch (error) { - // debugLogger('BioArInEO-PSO ERROR. ' + error.name + ' : ' + error.message) - // } - // for i in range(len(Swarm.multiparametric_space)): + var newStep = [] var particleCognitionLearning = 2.1 var particleSocialLearning = 1.1 var particleGlobalLearning = 1.6 var particleInertiaWeighting = 2 * 0.8 / (particleCognitionLearning + particleSocialLearning + particleGlobalLearning - 2) - debugLogger('BioArInEO-PSO evaluating new step') - var newStep = particleInertiaWeighting * particleStep + particleCognitionLearning * Math.random() * (particleBestPosition - particlePosition) + particleSocialLearning * Math.random() * (neighborsBestPosition - particlePosition) + particleGlobalLearning * Math.random() * (swarmBestPosition - particlePosition) - if (Math.abs(newStep) > maxStep) { - newStep = maxStep * (newStep > 0 ? 1 : -1) + var temporaryNeighborPosition + var neighborsPosition = [] + var neighborsFitness = [] + for (var index = 0, len = UserDefinedProtocol.controlledParameters.length; index < len; index++) { + debugLogger('BioArInEO-PSO evaluating new step for ' + UserDefinedProtocol.controlledParameters[index]) + if (len > 1) { + parametersSearchRange = UserDefinedProtocol.parametersSearchRange[index] + } + if (temporaryTest) { + particleStep.push(0.2 * (parametersSearchRange[1] - parametersSearchRange[0]) * (getRandomOnInterval(-1, 1) > 0 ? 1 : -1)) // ! PSI PBR JS doesn't support Math.sign() + } + debugLogger('BioArInEO-PSO particle step is ' + particleStep[index]) + maxStep.push(Number(UserDefinedProtocol.parametersMaxStep[index])) + debugLogger('BioArInEO-PSO particle max step is ' + maxStep[index]) + for (var indexN = 0, lenN = neighborsList.length; indexN < lenN; ++indexN) { + temporaryNeighborPosition = theServer.getGroupByName(neighborsList[indexN]).getAccessory('pumps.pump-5').context().get('particleLastPosition', undefined) + neighborsPosition = [] + neighborsFitness = [] + if (temporaryNeighborPosition === undefined) { + neighborsPosition.push(particlePosition[index]) + } else { + neighborsPosition.push(temporaryNeighborPosition[index]) + } + neighborsFitness.push(theServer.getGroupByName(neighborsList[indexN]).getAccessory('pumps.pump-5').context().get('particleLastFitness', particleFitness)) + } + neighborsBestFitness.push(Math.min.apply(null, neighborsFitness)) + neighborsBestPosition.push(Number(neighborsPosition[neighborsFitness.indexOf(neighborsBestFitness[index])])) + debugLogger('BioArInEO-PSO neighbors best position is [ ' + neighborsBestPosition[index] + '] with fitness ' + neighborsBestFitness[index]) + newStep.push(particleInertiaWeighting * particleStep[index] + particleCognitionLearning * Math.random() * (particleBestPosition[index] - particlePosition[index]) + particleSocialLearning * Math.random() * (neighborsBestPosition[index] - particlePosition[index]) + particleGlobalLearning * Math.random() * (swarmBestPosition[index] - particlePosition[index])) + if (Math.abs(newStep[index]) > maxStep[index]) { + newStep[index] = maxStep[index] * (newStep[index] > 0 ? 1 : -1) + } + if ((particlePosition[index] + newStep[index]) > UserDefinedProtocol.parametersSearchRange[1]) { + newPosition.push(UserDefinedProtocol.parametersSearchRange[1]) + } else if ((particlePosition[index] + newStep[index]) < UserDefinedProtocol.parametersSearchRange[0]) { + newPosition.push(UserDefinedProtocol.parametersSearchRange[0]) + } else { + newPosition.push(particlePosition[index] + newStep[index]) + } + controlParameter(UserDefinedProtocol.controlledParameters[index], newPosition[index]) } - theAccessory.context().put('particleStep', newStep) if (!(particleFitness > swarmBestFitness)) { theAccessory.context().put('particleBestPosition', particlePosition) theAccessory.context().put('particleBestFitness', particleFitness) @@ -538,15 +583,10 @@ function PSO (particleFitness) { theAccessory.context().put('particleBestPosition', particlePosition) theAccessory.context().put('particleBestFitness', particleFitness) } - if ((particlePosition + newStep) > parameterSearchRange[1]) { - newPosition.push(parameterSearchRange[1]) - } else if ((particlePosition + newStep) < parameterSearchRange[0]) { - newPosition.push(parameterSearchRange[0]) - } else { - newPosition.push(particlePosition + newStep) - } + theAccessory.context().put('particleStep', newStep) theAccessory.context().put('particlePosition', newPosition) - debugLogger('BioArInEO-PSO new step is ' + newStep + ' and position is ' + newPosition) - theServer.sendMail('PSO on ' + theGroup.getName() , 'NONE', ': new step is ' + newStep + ' and position is ' + newPosition) // Email notifications - return newPosition + debugLogger('BioArInEO-PSO best swarm position is [ ' + swarmBestPosition + ' ] with fitness ' + swarmBestFitness) + debugLogger('BioArInEO-PSO best neighbors position is [ ' + neighborsBestPosition + ' ] with fitness ' + neighborsBestFitness[0]) + debugLogger('BioArInEO-PSO new step is [ ' + newStep + ' ] and position is [ ' + newPosition + ' ]') + theServer.sendMail('PSO on ' + theGroup.getName() , 'NONE', ': new step is [ ' + newStep + ' ] and position is [ ' + newPosition + ' ]') // Email notifications } From c45610d42f0d991c5cbb7ba89b0dff71565a7c17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cerven=C3=BD?= Date: Fri, 28 Aug 2020 07:37:52 +0200 Subject: [PATCH 10/47] Modifications in to allow single channel GMS (CO2) change --- PP-GrowthOptimizer.js | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/PP-GrowthOptimizer.js b/PP-GrowthOptimizer.js index 9d93dd1..ea93055 100644 --- a/PP-GrowthOptimizer.js +++ b/PP-GrowthOptimizer.js @@ -251,6 +251,7 @@ function controlParameter (parameter, values) { var light0 = theGroup.getAccessory('actinic-lights.light-Red') var light1 = theGroup.getAccessory(theAccessory.context().get('light1String', 'actinic-lights.light-Blue')) unit = ' uE' + values = Array(values, values) light0.setRunningProtoConfig(new ProtoConfig(Number(values[0]))) // Red light1.setRunningProtoConfig(new ProtoConfig(Number(values[1]))) // Blue || White debugLogger('Lights changed. Channel 0 set to ' + round(values[0], 0) + unit + ' and channel 1 set to ' + round(values[1], 0) + unit) @@ -280,10 +281,19 @@ function controlParameter (parameter, values) { unit = ' ml/min' valve0.setRunningProtoConfig(new ProtoConfig(Number(values[0]))) valve1.setRunningProtoConfig(new ProtoConfig(Number(values[1]))) - var flowAir = valve0.getProtoConfigValue() - var flowCO2 = valve1.getProtoConfigValue() + var flowCO2 = valve0.getProtoConfigValue() + var flowAir = valve1.getProtoConfigValue() debugLogger('GMS settings changed. Gas Mixing set to Air flow ' + round(flowAir, 0) + unit + ' and CO2 flow ' + round(flowCO2, 1) + unit + ' (' + round((flowCO2 / (flowCO2 + flowAir) + 395 / 1e6) * 100, 1) + '%)') break + case 'GMS-CO2': + var valve0 = UserDefinedProtocol.groupGMS.getAccessory('gas-mixer.valve-0-reg') // CO2 + var valve1 = UserDefinedProtocol.groupGMS.getAccessory('gas-mixer.valve-1-reg') // Air + unit = ' ml/min' + valve0.setRunningProtoConfig(new ProtoConfig(Number(values))) + var flowCO2 = valve0.getProtoConfigValue() + var flowAir = valve1.getProtoConfigValue() + debugLogger('GMS settings changed. Gas Mixing set to Air flow ' + round(flowAir, 0) + unit + ' and CO2 flow ' + round(flowCO2, 1) + unit + ' (' + round((flowCO2 / (flowCO2 + flowAir) + 395 / 1e6) * 100, 1) + '%)') + break case 'stirrer': var stirrer = theGroup.getAccessory('pwm.stirrer') unit = '%' @@ -571,7 +581,7 @@ function PSO (particleFitness) { } else { newPosition.push(particlePosition[index] + newStep[index]) } - controlParameter(UserDefinedProtocol.controlledParameters[index], newPosition[index]) + controlParameter(UserDefinedProtocol.controlledParameters[index], round(newPosition[index], 2)) } if (!(particleFitness > swarmBestFitness)) { theAccessory.context().put('particleBestPosition', particlePosition) @@ -588,5 +598,5 @@ function PSO (particleFitness) { debugLogger('BioArInEO-PSO best swarm position is [ ' + swarmBestPosition + ' ] with fitness ' + swarmBestFitness) debugLogger('BioArInEO-PSO best neighbors position is [ ' + neighborsBestPosition + ' ] with fitness ' + neighborsBestFitness[0]) debugLogger('BioArInEO-PSO new step is [ ' + newStep + ' ] and position is [ ' + newPosition + ' ]') - theServer.sendMail('PSO on ' + theGroup.getName() , 'NONE', ': new step is [ ' + newStep + ' ] and position is [ ' + newPosition + ' ]') // Email notifications + theServer.sendMail('PSO on ' + theGroup.getName() , 'NONE', ': for fitness ' + particleFitness + ' new step is [ ' + newStep + ' ] and position is [ ' + newPosition + ' ]') // Email notifications } From 5d1171184a9ffabbdef986d7b66f9a098b2f34c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cerven=C3=BD?= Date: Tue, 1 Sep 2020 08:35:35 +0200 Subject: [PATCH 11/47] Adjusted fitness averaging when stabilizedTimeMax reached --- PP-GrowthOptimizer.js | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/PP-GrowthOptimizer.js b/PP-GrowthOptimizer.js index ea93055..9869e07 100644 --- a/PP-GrowthOptimizer.js +++ b/PP-GrowthOptimizer.js @@ -165,6 +165,10 @@ if (!theAccessory.context().getInt('initiated', 0)) { } theAccessory.context().put('OD7XYString', OD7XYString) } + if (UserDefinedProtocol.turbidostatODMin > UserDefinedProtocol.turbidostatODMax) { + debugLogger('OD range reversed.') + theExperiment.addEvent('OD range set in reversed order - will be automatically corrected.') + } controlParameter(UserDefinedProtocol.controlledParameter, UserDefinedProtocol.controlledParameterSteps[0]) theAccessory.context().put('initiated', 1) debugLogger('Peristaltic Pump - Growth Optimizer initialization successful.') @@ -365,15 +369,14 @@ function controlPump () { // Check for reversed OD range if (UserDefinedProtocol.turbidostatODMin > UserDefinedProtocol.turbidostatODMax) { UserDefinedProtocol.turbidostatODMin = (UserDefinedProtocol.turbidostatODMax - UserDefinedProtocol.turbidostatODMin) + (UserDefinedProtocol.turbidostatODMax = UserDefinedProtocol.turbidostatODMin) - debugLogger('OD range reversed.', 0) } if (theAccessory.context().getInt('stabilizedTimeMax', 0) <= Number(theExperiment.getDurationSec()) && (stepCounter !== 0)) { theAccessory.context().put('stabilizedTimeMax', theExperiment.getDurationSec() + UserDefinedProtocol.stabilizationTimeMax * 3600) // TODO allocate var for getDurationSec if (UserDefinedProtocol.particleSwarmOptimizer) { var stepDoublingTime = theAccessory.context().get('stepDoublingTime', [ 999.9 ]) var len = stepDoublingTime.length - stepDoublingTime = len > 2 ? stepDoublingTime.slice(1, len).reduce(getSumArrReduce, 0) / (len - 1) : stepDoublingTime[len - 1] - PSO(stepDoublingTime) + var stepDoublingTimeAvg = len > 2 ? len > UserDefinedProtocol.analyzedSteps ? stepDoublingTime.slice(len - UserDefinedProtocol.analyzedSteps, len).reduce(getSumArrReduce, 0) / (UserDefinedProtocol.analyzedSteps) : stepDoublingTime.slice(1, len).reduce(getSumArrReduce, 0) / (len - 1) : stepDoublingTime[len - 1] + PSO(stepDoublingTimeAvg) theAccessory.context().remove('stepCounter') theAccessory.context().remove('expDuration') theAccessory.context().remove('stepDoublingTime') @@ -533,7 +536,6 @@ function PSO (particleFitness) { temporaryTest = true particleStep = [] } - var maxStep = [] var neighborsBestPosition = [] var neighborsBestFitness = [] var newPosition = [] @@ -554,8 +556,7 @@ function PSO (particleFitness) { particleStep.push(0.2 * (parametersSearchRange[1] - parametersSearchRange[0]) * (getRandomOnInterval(-1, 1) > 0 ? 1 : -1)) // ! PSI PBR JS doesn't support Math.sign() } debugLogger('BioArInEO-PSO particle step is ' + particleStep[index]) - maxStep.push(Number(UserDefinedProtocol.parametersMaxStep[index])) - debugLogger('BioArInEO-PSO particle max step is ' + maxStep[index]) + debugLogger('BioArInEO-PSO particle max step is ' + UserDefinedProtocol.parametersMaxStep[index]) for (var indexN = 0, lenN = neighborsList.length; indexN < lenN; ++indexN) { temporaryNeighborPosition = theServer.getGroupByName(neighborsList[indexN]).getAccessory('pumps.pump-5').context().get('particleLastPosition', undefined) neighborsPosition = [] @@ -571,13 +572,13 @@ function PSO (particleFitness) { neighborsBestPosition.push(Number(neighborsPosition[neighborsFitness.indexOf(neighborsBestFitness[index])])) debugLogger('BioArInEO-PSO neighbors best position is [ ' + neighborsBestPosition[index] + '] with fitness ' + neighborsBestFitness[index]) newStep.push(particleInertiaWeighting * particleStep[index] + particleCognitionLearning * Math.random() * (particleBestPosition[index] - particlePosition[index]) + particleSocialLearning * Math.random() * (neighborsBestPosition[index] - particlePosition[index]) + particleGlobalLearning * Math.random() * (swarmBestPosition[index] - particlePosition[index])) - if (Math.abs(newStep[index]) > maxStep[index]) { - newStep[index] = maxStep[index] * (newStep[index] > 0 ? 1 : -1) + if (Math.abs(newStep[index]) > Number(UserDefinedProtocol.parametersMaxStep[index])) { + newStep[index] = Number(UserDefinedProtocol.parametersMaxStep[index]) * (newStep[index] > 0 ? 1 : -1) } - if ((particlePosition[index] + newStep[index]) > UserDefinedProtocol.parametersSearchRange[1]) { - newPosition.push(UserDefinedProtocol.parametersSearchRange[1]) - } else if ((particlePosition[index] + newStep[index]) < UserDefinedProtocol.parametersSearchRange[0]) { - newPosition.push(UserDefinedProtocol.parametersSearchRange[0]) + if ((particlePosition[index] + newStep[index]) > parametersSearchRange[1]) { + newPosition.push(parametersSearchRange[1]) + } else if ((particlePosition[index] + newStep[index]) < parametersSearchRange[0]) { + newPosition.push(parametersSearchRange[0]) } else { newPosition.push(particlePosition[index] + newStep[index]) } From 1603ec7f1758d1e758c4ca51313e0de252b05bcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cerven=C3=BD?= Date: Tue, 8 Sep 2020 06:45:37 +0200 Subject: [PATCH 12/47] Improved behavior for trigger of stabilizationTimeMax --- PP-GrowthOptimizer.js | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/PP-GrowthOptimizer.js b/PP-GrowthOptimizer.js index 9869e07..1fe8970 100644 --- a/PP-GrowthOptimizer.js +++ b/PP-GrowthOptimizer.js @@ -255,7 +255,6 @@ function controlParameter (parameter, values) { var light0 = theGroup.getAccessory('actinic-lights.light-Red') var light1 = theGroup.getAccessory(theAccessory.context().get('light1String', 'actinic-lights.light-Blue')) unit = ' uE' - values = Array(values, values) light0.setRunningProtoConfig(new ProtoConfig(Number(values[0]))) // Red light1.setRunningProtoConfig(new ProtoConfig(Number(values[1]))) // Blue || White debugLogger('Lights changed. Channel 0 set to ' + round(values[0], 0) + unit + ' and channel 1 set to ' + round(values[1], 0) + unit) @@ -370,17 +369,13 @@ function controlPump () { if (UserDefinedProtocol.turbidostatODMin > UserDefinedProtocol.turbidostatODMax) { UserDefinedProtocol.turbidostatODMin = (UserDefinedProtocol.turbidostatODMax - UserDefinedProtocol.turbidostatODMin) + (UserDefinedProtocol.turbidostatODMax = UserDefinedProtocol.turbidostatODMin) } - if (theAccessory.context().getInt('stabilizedTimeMax', 0) <= Number(theExperiment.getDurationSec()) && (stepCounter !== 0)) { + if (theAccessory.context().getInt('stabilizedTimeMax', 0) <= Number(theExperiment.getDurationSec()) && !pumpState) { theAccessory.context().put('stabilizedTimeMax', theExperiment.getDurationSec() + UserDefinedProtocol.stabilizationTimeMax * 3600) // TODO allocate var for getDurationSec + var stepDoublingTime = theAccessory.context().get('stepDoublingTime', [ 999.9 ]) if (UserDefinedProtocol.particleSwarmOptimizer) { - var stepDoublingTime = theAccessory.context().get('stepDoublingTime', [ 999.9 ]) var len = stepDoublingTime.length var stepDoublingTimeAvg = len > 2 ? len > UserDefinedProtocol.analyzedSteps ? stepDoublingTime.slice(len - UserDefinedProtocol.analyzedSteps, len).reduce(getSumArrReduce, 0) / (UserDefinedProtocol.analyzedSteps) : stepDoublingTime.slice(1, len).reduce(getSumArrReduce, 0) / (len - 1) : stepDoublingTime[len - 1] PSO(stepDoublingTimeAvg) - theAccessory.context().remove('stepCounter') - theAccessory.context().remove('expDuration') - theAccessory.context().remove('stepDoublingTime') - theAccessory.context().remove('stabilizedTime') } else if (UserDefinedProtocol.controlledParameterSteps.length > 1) { if (changeCounter < (UserDefinedProtocol.controlledParameterSteps.length - 1)) { controlParameter(UserDefinedProtocol.controlledParameter, UserDefinedProtocol.controlledParameterSteps[++changeCounter]) @@ -392,11 +387,11 @@ function controlPump () { controlParameter(UserDefinedProtocol.controlledParameter, UserDefinedProtocol.controlledParameterSteps[1]) theAccessory.context().put('changeCounter', 1) } - theAccessory.context().remove('stepCounter') - theAccessory.context().remove('expDuration') - theAccessory.context().remove('stepDoublingTime') - theAccessory.context().remove('stabilizedTime') - } + } + theAccessory.context().remove('stepCounter') + theAccessory.context().remove('expDuration') + theAccessory.context().remove('stepDoublingTime') + theAccessory.context().remove('stabilizedTime') } // Start step growth rate evaluation if (((odValue > (UserDefinedProtocol.turbidostatODMax * odMaxModifier)) && !pumpState)) { From 18384aca75bb9cd6972c8e51058ca4ae6d92b90a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cerven=C3=BD?= Date: Tue, 15 Sep 2020 10:12:15 +0200 Subject: [PATCH 13/47] Mail announcement for standard optimizer added --- PP-GrowthOptimizer.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/PP-GrowthOptimizer.js b/PP-GrowthOptimizer.js index 1fe8970..384db51 100644 --- a/PP-GrowthOptimizer.js +++ b/PP-GrowthOptimizer.js @@ -5,9 +5,9 @@ var UserDefinedProtocol = { turbidostatODType: 720, ODReadoutInterval: 60, // -optimizer parameters - controlledParameter: 'temperature', + controlledParameter: 'none', controlledParameterSteps: [ 28 ], - particleSwarmOptimizer: true, + particleSwarmOptimizer: false, controlledParameters: [ 'temperature' ], controlledParametersIC: [ 28 ], parametersSearchRange: [ 18, 38 ], @@ -45,8 +45,8 @@ var UserDefinedProtocol = { * @author CzechGlobe - Department of Adaptive Biotechnologies (JaCe) * @copyright Jan Červený 2020(c) * @license MIT - * @version 3.4.1 - * @modified 24.8.2020 (JaCe) + * @version 3.4.2 + * @modified 10.9.2020 (JaCe) * * @notes For proper functionality of the script "OD Regulator" protocol has to be disabled as well as chosen * controlled accessory protocols (i.e. Lights, Thermoregulation, GMS, Stirrer). @@ -99,6 +99,7 @@ importPackage(Packages.psi.bioreactor.core.regression) if (!theAccessory.context().getInt('initiated', 0)) { try { theAccessory.context().clear() + theAccessory.context().put('stabilizedTime', theExperiment.getDurationSec() + UserDefinedProtocol.stabilizationTimeMin * 3600) theAccessory.context().put('stabilizedTimeMax', theExperiment.getDurationSec() + UserDefinedProtocol.stabilizationTimeMax * 3600) var light1String if (theGroup.getAccessory('actinic-lights.light-Blue') === null) { @@ -475,6 +476,7 @@ function controlPump () { controlParameter(UserDefinedProtocol.controlledParameter, UserDefinedProtocol.controlledParameterSteps[1]) theAccessory.context().put('changeCounter', 1) } + theServer.sendMail('OPTIMIZER on ' + theGroup.getName() , 'NONE', ': for fitness ' + stepDoublingTimeAvg + ' set new position') // Email notifications } theAccessory.context().put('stabilizedTimeMax', theExperiment.getDurationSec() + UserDefinedProtocol.stabilizationTimeMax * 3600) theAccessory.context().remove('stepCounter') From 6c27998bd96566bd931d3496c798d25108827d97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cerven=C3=BD?= Date: Thu, 1 Oct 2020 09:36:53 +0200 Subject: [PATCH 14/47] Added PSO swarm best particle logging --- PP-GrowthOptimizer.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/PP-GrowthOptimizer.js b/PP-GrowthOptimizer.js index 384db51..ac5b6d3 100644 --- a/PP-GrowthOptimizer.js +++ b/PP-GrowthOptimizer.js @@ -525,6 +525,7 @@ function PSO (particleFitness) { var swarmLeader = theServer.getGroupByName(UserDefinedProtocol.swarmLeaderGroup) var swarmBestPosition = swarmLeader.getAccessory('pumps.pump-5').context().get('swarmBestPosition', particlePosition) var swarmBestFitness = swarmLeader.getAccessory('pumps.pump-5').context().get('swarmBestFitness', particleFitness) + var swarmBestParticle = swarmLeader.getAccessory('pumps.pump-5').context().get('swarmBestParticle', undefined) var neighborsList = UserDefinedProtocol.particleNeighborsGroupList var parametersSearchRange = UserDefinedProtocol.parametersSearchRange var particleStep = theAccessory.context().get('particleStep', undefined) @@ -567,7 +568,7 @@ function PSO (particleFitness) { } neighborsBestFitness.push(Math.min.apply(null, neighborsFitness)) neighborsBestPosition.push(Number(neighborsPosition[neighborsFitness.indexOf(neighborsBestFitness[index])])) - debugLogger('BioArInEO-PSO neighbors best position is [ ' + neighborsBestPosition[index] + '] with fitness ' + neighborsBestFitness[index]) + debugLogger('BioArInEO-PSO neighbors best position is [ ' + neighborsBestPosition[index] + ' ] with fitness ' + neighborsBestFitness[index]) newStep.push(particleInertiaWeighting * particleStep[index] + particleCognitionLearning * Math.random() * (particleBestPosition[index] - particlePosition[index]) + particleSocialLearning * Math.random() * (neighborsBestPosition[index] - particlePosition[index]) + particleGlobalLearning * Math.random() * (swarmBestPosition[index] - particlePosition[index])) if (Math.abs(newStep[index]) > Number(UserDefinedProtocol.parametersMaxStep[index])) { newStep[index] = Number(UserDefinedProtocol.parametersMaxStep[index]) * (newStep[index] > 0 ? 1 : -1) @@ -586,7 +587,8 @@ function PSO (particleFitness) { theAccessory.context().put('particleBestFitness', particleFitness) swarmLeader.getAccessory('pumps.pump-5').context().put('swarmBestPosition', particlePosition) swarmLeader.getAccessory('pumps.pump-5').context().put('swarmBestFitness', particleFitness) - swarmLeader.getAccessory('pumps.pump-5').context().put('swarmBestParticle', theGroup) + swarmBestParticle = theGroup + swarmLeader.getAccessory('pumps.pump-5').context().put('swarmBestParticle', swarmBestParticle) } else if (!(particleFitness > particleBestFitness)) { theAccessory.context().put('particleBestPosition', particlePosition) theAccessory.context().put('particleBestFitness', particleFitness) @@ -594,6 +596,7 @@ function PSO (particleFitness) { theAccessory.context().put('particleStep', newStep) theAccessory.context().put('particlePosition', newPosition) debugLogger('BioArInEO-PSO best swarm position is [ ' + swarmBestPosition + ' ] with fitness ' + swarmBestFitness) + debugLogger('BioArInEO-PSO best swarm particle is ' + swarmBestParticle) debugLogger('BioArInEO-PSO best neighbors position is [ ' + neighborsBestPosition + ' ] with fitness ' + neighborsBestFitness[0]) debugLogger('BioArInEO-PSO new step is [ ' + newStep + ' ] and position is [ ' + newPosition + ' ]') theServer.sendMail('PSO on ' + theGroup.getName() , 'NONE', ': for fitness ' + particleFitness + ' new step is [ ' + newStep + ' ] and position is [ ' + newPosition + ' ]') // Email notifications From f1a7271a80fa999561b9ecd51665fefeca3a5fe9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cerven=C3=BD?= Date: Sat, 3 Oct 2020 23:29:00 +0200 Subject: [PATCH 15/47] Correction of IC95 evalution to respect population sample limit --- PP-GrowthOptimizer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PP-GrowthOptimizer.js b/PP-GrowthOptimizer.js index ac5b6d3..ad65e1a 100644 --- a/PP-GrowthOptimizer.js +++ b/PP-GrowthOptimizer.js @@ -445,7 +445,7 @@ function controlPump () { for (i = (stepCounter - 1); i >= (stepCounter - UserDefinedProtocol.analyzedSteps); i--) { stepDoublingTimeSD += Math.pow(stepDoublingTime[i] - stepDoublingTimeAvg, 2) } - stepDoublingTimeSD = Math.sqrt(stepDoublingTimeSD / UserDefinedProtocol.analyzedSteps) + stepDoublingTimeSD = Math.sqrt(stepDoublingTimeSD / (UserDefinedProtocol.analyzedSteps - 1)) stepDoublingTimeIC95 = stepDoublingTimeSD / Math.sqrt(UserDefinedProtocol.analyzedSteps) * 1.96 // Trend of steps doubling time for (i = (stepCounter - 1); i >= (stepCounter - UserDefinedProtocol.analyzedSteps); i--) { From c496f48e898aa1d3d9b0f2df72fc38ca527e1a62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cerven=C3=BD?= Date: Wed, 7 Oct 2020 08:21:00 +0200 Subject: [PATCH 16/47] Fixed GMS default help parameters set --- PP-GrowthOptimizer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PP-GrowthOptimizer.js b/PP-GrowthOptimizer.js index ad65e1a..24673a8 100644 --- a/PP-GrowthOptimizer.js +++ b/PP-GrowthOptimizer.js @@ -61,8 +61,8 @@ var UserDefinedProtocol = { * @param {string} controlledParameter ['none'/'temperature'/'lights'/'GMS'/'stirrer'/'ODRange'] - Supported parameters to control by the script * @param {array} controlledParameterSteps - List of values for the controlled parameter. Examples: * temperature = [ 28, 32, 34, 30, 26, 22 ]; // [oC] - * lights = [[ 55, 25 ],[ 110, 25 ],[ 220, 25 ],[ 440, 25 ],[ 880,25 ]]; // [uE] - * GMS = [[ 195.88, 5.873 ],[ 195.88, 12.478 ],[ 185.30, 18.257 ],[ 185.30,25.274 ]]; // [ml/min] + * lights = [[ 55, 25 ], [ 110, 25 ], [ 220, 25 ], [ 440, 25 ], [ 880,25 ]]; // [uE] + * GMS = [[ 9.6, 990 ], [ 19.6, 1880 ], [ 39.6, 1940 ]]; // [ml/min] * stirrer = [ 30, 50, 65, 80, 95 ]; // [%] !!! works only with SW version 0.7.14 and later * ODRange = [[0.4, 0.425], [0.2, 0.215], [0.1, 0.113]]; // [AU] * -optimizer stability check From 3bd7453a9ad084d58dc2e809b8022373d746bf4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cerven=C3=BD?= Date: Fri, 23 Oct 2020 16:06:43 +0200 Subject: [PATCH 17/47] Updated list for handling of control parameters warnings --- PP-GrowthOptimizer.js | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/PP-GrowthOptimizer.js b/PP-GrowthOptimizer.js index 24673a8..2f754cb 100644 --- a/PP-GrowthOptimizer.js +++ b/PP-GrowthOptimizer.js @@ -45,8 +45,8 @@ var UserDefinedProtocol = { * @author CzechGlobe - Department of Adaptive Biotechnologies (JaCe) * @copyright Jan Červený 2020(c) * @license MIT - * @version 3.4.2 - * @modified 10.9.2020 (JaCe) + * @version 3.4.3 + * @modified 7.10.2020 (JaCe) * * @notes For proper functionality of the script "OD Regulator" protocol has to be disabled as well as chosen * controlled accessory protocols (i.e. Lights, Thermoregulation, GMS, Stirrer). @@ -121,6 +121,21 @@ if (!theAccessory.context().getInt('initiated', 0)) { theExperiment.addEvent('!!! Disable WHITE LIGHT protocol') } break + case 'light-red': + if (theGroup.getAccessory('actinic-lights.light-Red').getProtoConfigValue()) { + theExperiment.addEvent('!!! Disable RED LIGHT protocol') + } + break + case 'light-blue': + if (theGroup.getAccessory('actinic-lights.light-Blue').getProtoConfigValue()) { + theExperiment.addEvent('!!! Disable RED LIGHT protocol') + } + break + case 'light-white': + if (theGroup.getAccessory('actinic-lights.light-Blue').getProtoConfigValue()) { + theExperiment.addEvent('!!! Disable RED LIGHT protocol') + } + break case 'temperature': if (theGroup.getAccessory('thermo.thermo-reg').getProtoConfigValue()) { theExperiment.addEvent('!!! Disable THERMOREGULATOR protocol') @@ -134,6 +149,11 @@ if (!theAccessory.context().getInt('initiated', 0)) { theExperiment.addEvent('!!! Disable GMS Air/N2 protocol') } break + case 'GMS-CO2': + if (UserDefinedProtocol.groupGMS.getAccessory('gas-mixer.valve-0-reg').getProtoConfigValue()) { + theExperiment.addEvent('!!! Disable GMS CO2 protocol') + } + break case 'stirrer': if (theGroup.getAccessory('pwm.stirrer').getProtoConfigValue()) { theExperiment.addEvent('!!! Disable STIRRER protocol') @@ -289,7 +309,7 @@ function controlParameter (parameter, values) { var flowAir = valve1.getProtoConfigValue() debugLogger('GMS settings changed. Gas Mixing set to Air flow ' + round(flowAir, 0) + unit + ' and CO2 flow ' + round(flowCO2, 1) + unit + ' (' + round((flowCO2 / (flowCO2 + flowAir) + 395 / 1e6) * 100, 1) + '%)') break - case 'GMS-CO2': + case 'GMS-CO2': var valve0 = UserDefinedProtocol.groupGMS.getAccessory('gas-mixer.valve-0-reg') // CO2 var valve1 = UserDefinedProtocol.groupGMS.getAccessory('gas-mixer.valve-1-reg') // Air unit = ' ml/min' From 735f195738eb4adff253957e83e2fe76b5c83748 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cerven=C3=BD?= Date: Sat, 24 Oct 2020 22:37:08 +0200 Subject: [PATCH 18/47] Added logging for uncorrected new step --- PP-GrowthOptimizer.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/PP-GrowthOptimizer.js b/PP-GrowthOptimizer.js index 2f754cb..338814e 100644 --- a/PP-GrowthOptimizer.js +++ b/PP-GrowthOptimizer.js @@ -45,8 +45,8 @@ var UserDefinedProtocol = { * @author CzechGlobe - Department of Adaptive Biotechnologies (JaCe) * @copyright Jan Červený 2020(c) * @license MIT - * @version 3.4.3 - * @modified 7.10.2020 (JaCe) + * @version 3.4.4 + * @modified 23.10.2020 (JaCe) * * @notes For proper functionality of the script "OD Regulator" protocol has to be disabled as well as chosen * controlled accessory protocols (i.e. Lights, Thermoregulation, GMS, Stirrer). @@ -588,8 +588,9 @@ function PSO (particleFitness) { } neighborsBestFitness.push(Math.min.apply(null, neighborsFitness)) neighborsBestPosition.push(Number(neighborsPosition[neighborsFitness.indexOf(neighborsBestFitness[index])])) - debugLogger('BioArInEO-PSO neighbors best position is [ ' + neighborsBestPosition[index] + ' ] with fitness ' + neighborsBestFitness[index]) + debugLogger('BioArInEO-PSO neighbors best position for ' + UserDefinedProtocol.controlledParameters[index] + ' is [ ' + neighborsBestPosition[index] + ' ] with fitness ' + neighborsBestFitness[index]) newStep.push(particleInertiaWeighting * particleStep[index] + particleCognitionLearning * Math.random() * (particleBestPosition[index] - particlePosition[index]) + particleSocialLearning * Math.random() * (neighborsBestPosition[index] - particlePosition[index]) + particleGlobalLearning * Math.random() * (swarmBestPosition[index] - particlePosition[index])) + debugLogger('BioArInEO-PSO new uncorrected step for ' + UserDefinedProtocol.controlledParameters[index] + ' is [ ' + newStep[index] + ' ]') if (Math.abs(newStep[index]) > Number(UserDefinedProtocol.parametersMaxStep[index])) { newStep[index] = Number(UserDefinedProtocol.parametersMaxStep[index]) * (newStep[index] > 0 ? 1 : -1) } From a349c34449266519c33e914f15f1180872ba984b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cerven=C3=BD?= Date: Tue, 27 Oct 2020 15:53:45 +0100 Subject: [PATCH 19/47] PSO logging improvements --- PP-GrowthOptimizer.js | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/PP-GrowthOptimizer.js b/PP-GrowthOptimizer.js index 338814e..8a0b25c 100644 --- a/PP-GrowthOptimizer.js +++ b/PP-GrowthOptimizer.js @@ -45,8 +45,8 @@ var UserDefinedProtocol = { * @author CzechGlobe - Department of Adaptive Biotechnologies (JaCe) * @copyright Jan Červený 2020(c) * @license MIT - * @version 3.4.4 - * @modified 23.10.2020 (JaCe) + * @version 3.4.5 + * @modified 26.10.2020 (JaCe) * * @notes For proper functionality of the script "OD Regulator" protocol has to be disabled as well as chosen * controlled accessory protocols (i.e. Lights, Thermoregulation, GMS, Stirrer). @@ -456,6 +456,7 @@ function controlPump () { var sumY = 0 var sumX2 = 0 // var sumY2 = 0 + // TODO if trend then accumulate steps as [analyzedSteps inf] // Average of steps doubling time for (var i = (stepCounter - 1); i >= (stepCounter - UserDefinedProtocol.analyzedSteps); i--) { stepDoublingTimeAvg += Number(stepDoublingTime[i]) @@ -538,7 +539,7 @@ function PSO (particleFitness) { } theAccessory.context().put('particleLastFitness', particleFitness) var particlePosition = theAccessory.context().get('particlePosition', UserDefinedProtocol.controlledParametersIC) - debugLogger('BioArInEO-PSO executed with fitness ' + particleFitness + ' and position ' + particlePosition) + debugLogger('BioArInEO-PSO executed with fitness ' + particleFitness + ' and position [ ' + particlePosition + ' ]') theAccessory.context().put('particleLastPosition', particlePosition) var particleBestPosition = theAccessory.context().get('particleBestPosition', particlePosition) var particleBestFitness = theAccessory.context().get('particleBestFitness', particleFitness) @@ -566,15 +567,13 @@ function PSO (particleFitness) { var neighborsPosition = [] var neighborsFitness = [] for (var index = 0, len = UserDefinedProtocol.controlledParameters.length; index < len; index++) { - debugLogger('BioArInEO-PSO evaluating new step for ' + UserDefinedProtocol.controlledParameters[index]) if (len > 1) { parametersSearchRange = UserDefinedProtocol.parametersSearchRange[index] } if (temporaryTest) { particleStep.push(0.2 * (parametersSearchRange[1] - parametersSearchRange[0]) * (getRandomOnInterval(-1, 1) > 0 ? 1 : -1)) // ! PSI PBR JS doesn't support Math.sign() } - debugLogger('BioArInEO-PSO particle step is ' + particleStep[index]) - debugLogger('BioArInEO-PSO particle max step is ' + UserDefinedProtocol.parametersMaxStep[index]) + debugLogger('BioArInEO-PSO particle step for ' + UserDefinedProtocol.controlledParameters[index] + ' is ' + particleStep[index] + ' (max ' + UserDefinedProtocol.parametersMaxStep[index] + ' )') for (var indexN = 0, lenN = neighborsList.length; indexN < lenN; ++indexN) { temporaryNeighborPosition = theServer.getGroupByName(neighborsList[indexN]).getAccessory('pumps.pump-5').context().get('particleLastPosition', undefined) neighborsPosition = [] @@ -588,9 +587,9 @@ function PSO (particleFitness) { } neighborsBestFitness.push(Math.min.apply(null, neighborsFitness)) neighborsBestPosition.push(Number(neighborsPosition[neighborsFitness.indexOf(neighborsBestFitness[index])])) - debugLogger('BioArInEO-PSO neighbors best position for ' + UserDefinedProtocol.controlledParameters[index] + ' is [ ' + neighborsBestPosition[index] + ' ] with fitness ' + neighborsBestFitness[index]) + debugLogger('BioArInEO-PSO neighbors best position for ' + UserDefinedProtocol.controlledParameters[index] + ' is ' + neighborsBestPosition[index] + ' with fitness ' + neighborsBestFitness[index]) newStep.push(particleInertiaWeighting * particleStep[index] + particleCognitionLearning * Math.random() * (particleBestPosition[index] - particlePosition[index]) + particleSocialLearning * Math.random() * (neighborsBestPosition[index] - particlePosition[index]) + particleGlobalLearning * Math.random() * (swarmBestPosition[index] - particlePosition[index])) - debugLogger('BioArInEO-PSO new uncorrected step for ' + UserDefinedProtocol.controlledParameters[index] + ' is [ ' + newStep[index] + ' ]') + debugLogger('BioArInEO-PSO new uncorrected step for ' + UserDefinedProtocol.controlledParameters[index] + ' is ' + newStep[index]) if (Math.abs(newStep[index]) > Number(UserDefinedProtocol.parametersMaxStep[index])) { newStep[index] = Number(UserDefinedProtocol.parametersMaxStep[index]) * (newStep[index] > 0 ? 1 : -1) } From 16e5f61252905de3a905b3c095623d864019385c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cerven=C3=BD?= Date: Tue, 27 Oct 2020 23:06:50 +0100 Subject: [PATCH 20/47] Improved logging of PSO numbers (rounding toFixed(2)) --- PP-GrowthOptimizer.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/PP-GrowthOptimizer.js b/PP-GrowthOptimizer.js index 8a0b25c..c7ee134 100644 --- a/PP-GrowthOptimizer.js +++ b/PP-GrowthOptimizer.js @@ -539,7 +539,7 @@ function PSO (particleFitness) { } theAccessory.context().put('particleLastFitness', particleFitness) var particlePosition = theAccessory.context().get('particlePosition', UserDefinedProtocol.controlledParametersIC) - debugLogger('BioArInEO-PSO executed with fitness ' + particleFitness + ' and position [ ' + particlePosition + ' ]') + debugLogger('BioArInEO-PSO executed with fitness ' + particleFitness.toFixed(2) + ' and position [ ' + particlePosition.map(function(ae){return ae.toFixed(2)}) + ' ]') theAccessory.context().put('particleLastPosition', particlePosition) var particleBestPosition = theAccessory.context().get('particleBestPosition', particlePosition) var particleBestFitness = theAccessory.context().get('particleBestFitness', particleFitness) @@ -573,7 +573,7 @@ function PSO (particleFitness) { if (temporaryTest) { particleStep.push(0.2 * (parametersSearchRange[1] - parametersSearchRange[0]) * (getRandomOnInterval(-1, 1) > 0 ? 1 : -1)) // ! PSI PBR JS doesn't support Math.sign() } - debugLogger('BioArInEO-PSO particle step for ' + UserDefinedProtocol.controlledParameters[index] + ' is ' + particleStep[index] + ' (max ' + UserDefinedProtocol.parametersMaxStep[index] + ' )') + debugLogger('BioArInEO-PSO particle step for ' + UserDefinedProtocol.controlledParameters[index] + ' is ' + particleStep[index].toFixed(2) + ' (max ' + UserDefinedProtocol.parametersMaxStep[index].toFixed(2) + ' )') for (var indexN = 0, lenN = neighborsList.length; indexN < lenN; ++indexN) { temporaryNeighborPosition = theServer.getGroupByName(neighborsList[indexN]).getAccessory('pumps.pump-5').context().get('particleLastPosition', undefined) neighborsPosition = [] @@ -587,9 +587,9 @@ function PSO (particleFitness) { } neighborsBestFitness.push(Math.min.apply(null, neighborsFitness)) neighborsBestPosition.push(Number(neighborsPosition[neighborsFitness.indexOf(neighborsBestFitness[index])])) - debugLogger('BioArInEO-PSO neighbors best position for ' + UserDefinedProtocol.controlledParameters[index] + ' is ' + neighborsBestPosition[index] + ' with fitness ' + neighborsBestFitness[index]) + debugLogger('BioArInEO-PSO neighbors best position for ' + UserDefinedProtocol.controlledParameters[index] + ' is ' + neighborsBestPosition[index].toFixed(2) + ' with fitness ' + neighborsBestFitness[index].toFixed(2)) newStep.push(particleInertiaWeighting * particleStep[index] + particleCognitionLearning * Math.random() * (particleBestPosition[index] - particlePosition[index]) + particleSocialLearning * Math.random() * (neighborsBestPosition[index] - particlePosition[index]) + particleGlobalLearning * Math.random() * (swarmBestPosition[index] - particlePosition[index])) - debugLogger('BioArInEO-PSO new uncorrected step for ' + UserDefinedProtocol.controlledParameters[index] + ' is ' + newStep[index]) + debugLogger('BioArInEO-PSO new uncorrected step for ' + UserDefinedProtocol.controlledParameters[index] + ' is ' + newStep[index].toFixed(2)) if (Math.abs(newStep[index]) > Number(UserDefinedProtocol.parametersMaxStep[index])) { newStep[index] = Number(UserDefinedProtocol.parametersMaxStep[index]) * (newStep[index] > 0 ? 1 : -1) } @@ -615,9 +615,9 @@ function PSO (particleFitness) { } theAccessory.context().put('particleStep', newStep) theAccessory.context().put('particlePosition', newPosition) - debugLogger('BioArInEO-PSO best swarm position is [ ' + swarmBestPosition + ' ] with fitness ' + swarmBestFitness) + debugLogger('BioArInEO-PSO best swarm position is [ ' + swarmBestPosition.map(function(ae){return ae.toFixed(2)}) + ' ] with fitness ' + swarmBestFitness.toFixed(2)) debugLogger('BioArInEO-PSO best swarm particle is ' + swarmBestParticle) - debugLogger('BioArInEO-PSO best neighbors position is [ ' + neighborsBestPosition + ' ] with fitness ' + neighborsBestFitness[0]) - debugLogger('BioArInEO-PSO new step is [ ' + newStep + ' ] and position is [ ' + newPosition + ' ]') - theServer.sendMail('PSO on ' + theGroup.getName() , 'NONE', ': for fitness ' + particleFitness + ' new step is [ ' + newStep + ' ] and position is [ ' + newPosition + ' ]') // Email notifications + debugLogger('BioArInEO-PSO best neighbors position is [ ' + neighborsBestPosition.map(function(ae){return ae.toFixed(2)}) + ' ] with fitness ' + neighborsBestFitness[0].toFixed(2)) + debugLogger('BioArInEO-PSO new step is [ ' + newStep.map(function(ae){return ae.toFixed(2)}) + ' ] and position is [ ' + newPosition.map(function(ae){return ae.toFixed(2)}) + ' ]') + theServer.sendMail('PSO on ' + theGroup.getName() , 'NONE', ': for fitness ' + particleFitness.toFixed(2) + ' new step is [ ' + newStep.toFixed(2) + ' ] and position is [ ' + newPosition.map(function(ae){return ae.toFixed(2)}) + ' ]') // Email notifications } From d5fa51771df9bc69d48365c4ed3c464a1aaaf5b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cerven=C3=BD?= Date: Wed, 28 Oct 2020 18:37:05 +0100 Subject: [PATCH 21/47] Fixed array toFixed() for PSO email notification --- PP-GrowthOptimizer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PP-GrowthOptimizer.js b/PP-GrowthOptimizer.js index c7ee134..d08c9d2 100644 --- a/PP-GrowthOptimizer.js +++ b/PP-GrowthOptimizer.js @@ -619,5 +619,5 @@ function PSO (particleFitness) { debugLogger('BioArInEO-PSO best swarm particle is ' + swarmBestParticle) debugLogger('BioArInEO-PSO best neighbors position is [ ' + neighborsBestPosition.map(function(ae){return ae.toFixed(2)}) + ' ] with fitness ' + neighborsBestFitness[0].toFixed(2)) debugLogger('BioArInEO-PSO new step is [ ' + newStep.map(function(ae){return ae.toFixed(2)}) + ' ] and position is [ ' + newPosition.map(function(ae){return ae.toFixed(2)}) + ' ]') - theServer.sendMail('PSO on ' + theGroup.getName() , 'NONE', ': for fitness ' + particleFitness.toFixed(2) + ' new step is [ ' + newStep.toFixed(2) + ' ] and position is [ ' + newPosition.map(function(ae){return ae.toFixed(2)}) + ' ]') // Email notifications + theServer.sendMail('PSO on ' + theGroup.getName() , 'NONE', ': for fitness ' + particleFitness.toFixed(2) + ' new step is [ ' + newStep.map(function(ae){return ae.toFixed(2)}) + ' ] and position is [ ' + newPosition.map(function(ae){return ae.toFixed(2)}) + ' ]') // Email notifications } From 3c7e01f8fbf8053de1624cac32cb29e8fe74cbb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cerven=C3=BD?= Date: Thu, 19 Nov 2020 20:03:55 +0100 Subject: [PATCH 22/47] Implementation of try-catch error mechanism --- O2-PIcurveMeasurement.js | 221 ++++++++++++++++++++------------------- 1 file changed, 114 insertions(+), 107 deletions(-) diff --git a/O2-PIcurveMeasurement.js b/O2-PIcurveMeasurement.js index 4a8bf52..43d7fe2 100644 --- a/O2-PIcurveMeasurement.js +++ b/O2-PIcurveMeasurement.js @@ -23,10 +23,10 @@ var UserDefinedProtocol = { * * @script PI-Curves Measurement - Photosynthesis Efficiency Quantification * @author CzechGlobe - Department of Adaptive Biotechnologies (JaCe) - * @copyright Jan Červený 2019(c) + * @copyright Jan Červený 2020(c) * @license MIT - * @version 1.2.3 - * @modified 16.3.2019 (JaCe) + * @version 1.2.4 + * @modified 14.11.2020 (JaCe) * @notes For proper function of the script following protocols have to be disabled: "Lights", "Bubble intr. valve" and "Stirrer" * * @param {number} oxygenMeasurementDuration [s] Duration of O2 evolution measurement @@ -70,120 +70,127 @@ function controlLights (intensityRed, intensityBlue) { light0.setRunningProtoConfig(new ProtoConfig(intensityRed)) light1.setRunningProtoConfig(new ProtoConfig(intensityBlue)) } -if (!theAccessory.context().getBool('initialization', false)) { - theAccessory.context().clear() - if (theGroup.getAccessory('pwm.stirrer').getProtoConfigValue()) { - theExperiment.addEvent('!!! Disable stirrer protocol.') - } - if (theGroup.getAccessory('switches.valve-0').getProtoConfigValue()) { - theExperiment.addEvent('!!! Disable bubble intr. valve protocol.') - } - theAccessory.getDataHistory().setCapacity(Math.max(UserDefinedProtocol.oxygenMeasurementDuration, UserDefinedProtocol.respirationMeasurementDuration)) - theAccessory.context().put('rateO2Evol', []) - theAccessory.context().put('rateO2EvolR2', []) - theAccessory.context().put('rateO2Resp', []) - theAccessory.context().put('rateO2RespR2', []) - theAccessory.context().put('initialization', true) - theGroup.getAccessory('pwm.stirrer').setRunningProtoConfig(new ProtoConfig(UserDefinedProtocol.stirrerIntensityValues[0])) - theGroup.getAccessory('switches.valve-0').setRunningProtoConfig(ProtoConfig.ON) - measurementTime = experimentDuration + 600 - theAccessory.context().put('measurementTime', measurementTime) - var light1String - if (theGroup.getAccessory('actinic-lights.light-Blue') === null) { - light1String = 'actinic-lights.light-White' - } else { - light1String = 'actinic-lights.light-Blue' +if (!theAccessory.context().getBool('initiated', false)) { + try { + theAccessory.context().clear() + if (theGroup.getAccessory('pwm.stirrer').getProtoConfigValue()) { + theExperiment.addEvent('!!! Disable stirrer protocol.') + } + if (theGroup.getAccessory('switches.valve-0').getProtoConfigValue()) { + theExperiment.addEvent('!!! Disable bubble intr. valve protocol.') + } + theAccessory.getDataHistory().setCapacity(Math.max(UserDefinedProtocol.oxygenMeasurementDuration, UserDefinedProtocol.respirationMeasurementDuration)) + theAccessory.context().put('rateO2Evol', []) + theAccessory.context().put('rateO2EvolR2', []) + theAccessory.context().put('rateO2Resp', []) + theAccessory.context().put('rateO2RespR2', []) + theAccessory.context().put('initiated', true) + theGroup.getAccessory('pwm.stirrer').setRunningProtoConfig(new ProtoConfig(UserDefinedProtocol.stirrerIntensityValues[0])) + theGroup.getAccessory('switches.valve-0').setRunningProtoConfig(ProtoConfig.ON) + measurementTime = experimentDuration + 600 + theAccessory.context().put('measurementTime', measurementTime) + var light1String + if (theGroup.getAccessory('actinic-lights.light-Blue') === null) { + light1String = 'actinic-lights.light-White' + } else { + light1String = 'actinic-lights.light-Blue' + } + theAccessory.context().put('light1String', light1String) + debugLogger('Photosynthesis Efficiency Quantification initialization successful.') + } catch (error) { + debugLogger('Initialization ERROR. ' + error.name + ' : ' + error.message) } - theAccessory.context().put('light1String', light1String) - debugLogger('PI-Curves Measurement - Photosynthesis Efficiency Quantification initialization successful.') } if (experimentDuration >= measurementTime) { - var stirrer, bubbles - var turbidostat = theGroup.getAccessory('pumps.pump-5') - var dilution = UserDefinedProtocol.turbidostatSynchronization ? turbidostat.context().getInt('modeDilution', 0) : 1 - var stabilized = UserDefinedProtocol.growthStabilitySynchronization ? turbidostat.context().getInt('modeStabilized', 0) : 1 - if (dilution && stabilized) { - var regCoefLin, rateO2Evol, rateO2Resp, rateO2EvolR2, rateO2RespR2 - var changeCounter = theAccessory.context().getInt('changeCounter', 0) - var multiplierStep = theAccessory.context().getInt('multiplierStep', 0) - var resumeTime = theAccessory.context().getInt('resumeTime', 0) - // PI-curve phase parameters - var bubblingSuspended = theAccessory.context().getInt('bubblingSuspended', 0) - var photosynthesis = theAccessory.context().getInt('photosynthesis', 0) - var respiration = theAccessory.context().getInt('respiration', 0) - // Accessories inicialization - stirrer = theGroup.getAccessory('pwm.stirrer') - bubbles = theGroup.getAccessory('switches.valve-0') - var light0 = theGroup.getAccessory('actinic-lights.light-Red') - var light1 = theGroup.getAccessory(theAccessory.context().get('light1String', 'actinic-lights.light-Blue')) - if (!bubblingSuspended) { - theAccessory.context().put('bubblingSuspended', 1) - theAccessory.context().put('modeO2EvolResp', 1) - resumeTime = experimentDuration + UserDefinedProtocol.oxygenMeasurementDuration + UserDefinedProtocol.respirationMeasurementDuration - theAccessory.context().put('resumeTime', resumeTime) - theAccessory.context().put('light0Value', light0.getValue()) - theAccessory.context().put('light1Value', light1.getValue()) - bubbles.setRunningProtoConfig(ProtoConfig.OFF) - stirrer.setRunningProtoConfig(new ProtoConfig(UserDefinedProtocol.stirrerIntensityValues[1])) - controlLights(light0.getValue() * UserDefinedProtocol.lightStepMultiplierValues[changeCounter] * UserDefinedProtocol.photosynthesisCurveLightMultiplierValues[multiplierStep], light1.getValue() * UserDefinedProtocol.lightStepMultiplierValues[changeCounter] * UserDefinedProtocol.photosynthesisCurveLightMultiplierValues[multiplierStep]) - } - if ((experimentDuration > (resumeTime - UserDefinedProtocol.respirationMeasurementDuration)) && !photosynthesis) { - theAccessory.context().put('photosynthesis', 1) - if (UserDefinedProtocol.respirationMeasurementDuration > 0) { - light0.suspend(resumeTime) - light1.suspend(resumeTime) + try { + var stirrer, bubbles + var turbidostat = theGroup.getAccessory('pumps.pump-5') + var dilution = UserDefinedProtocol.turbidostatSynchronization ? turbidostat.context().getInt('modeDilution', 0) : 1 + var stabilized = UserDefinedProtocol.growthStabilitySynchronization ? turbidostat.context().getInt('modeStabilized', 0) : 1 + if (dilution && stabilized) { + var regCoefLin, rateO2Evol, rateO2Resp, rateO2EvolR2, rateO2RespR2 + var changeCounter = theAccessory.context().getInt('changeCounter', 0) + var multiplierStep = theAccessory.context().getInt('multiplierStep', 0) + var resumeTime = theAccessory.context().getInt('resumeTime', 0) + // PI-curve phase parameters + var bubblingSuspended = theAccessory.context().getInt('bubblingSuspended', 0) + var photosynthesis = theAccessory.context().getInt('photosynthesis', 0) + var respiration = theAccessory.context().getInt('respiration', 0) + // Accessories inicialization + stirrer = theGroup.getAccessory('pwm.stirrer') + bubbles = theGroup.getAccessory('switches.valve-0') + var light0 = theGroup.getAccessory('actinic-lights.light-Red') + var light1 = theGroup.getAccessory(theAccessory.context().get('light1String', 'actinic-lights.light-Blue')) + if (!bubblingSuspended) { + theAccessory.context().put('bubblingSuspended', 1) + theAccessory.context().put('modeO2EvolResp', 1) + resumeTime = experimentDuration + UserDefinedProtocol.oxygenMeasurementDuration + UserDefinedProtocol.respirationMeasurementDuration + theAccessory.context().put('resumeTime', resumeTime) + theAccessory.context().put('light0Value', light0.getValue()) + theAccessory.context().put('light1Value', light1.getValue()) + bubbles.setRunningProtoConfig(ProtoConfig.OFF) + stirrer.setRunningProtoConfig(new ProtoConfig(UserDefinedProtocol.stirrerIntensityValues[1])) + controlLights(light0.getValue() * UserDefinedProtocol.lightStepMultiplierValues[changeCounter] * UserDefinedProtocol.photosynthesisCurveLightMultiplierValues[multiplierStep], light1.getValue() * UserDefinedProtocol.lightStepMultiplierValues[changeCounter] * UserDefinedProtocol.photosynthesisCurveLightMultiplierValues[multiplierStep]) } - regCoefLin = theAccessory.getDataHistory().regression(ETrendFunction.LIN, Math.ceil(UserDefinedProtocol.photosynthesisRateCurveEvalFraction * UserDefinedProtocol.oxygenMeasurementDuration / UserDefinedProtocol.oxygenRapidMeasurementInterval)) - debugLogger('O2 evol. parameters: ' + regCoefLin.join(', ')) - rateO2Evol = theAccessory.context().get('rateO2Evol', []) - rateO2EvolR2 = theAccessory.context().get('rateO2EvolR2', []) - rateO2Evol[changeCounter] = round(regCoefLin[1] * 600, 2) - rateO2EvolR2[changeCounter] = round(regCoefLin[2], 3) - // TODO should be function1 - } - if ((experimentDuration > resumeTime) && !respiration) { - theAccessory.context().put('respiration', 1) - bubbles.setRunningProtoConfig(ProtoConfig.ON) - stirrer.setRunningProtoConfig(new ProtoConfig(UserDefinedProtocol.stirrerIntensityValues[0])) - if (UserDefinedProtocol.respirationMeasurementDuration > 0) { - light0.resume(experimentDuration) - light1.resume(experimentDuration) - controlLights(theAccessory.context().getDouble('light0Value', light0.getValue()), theAccessory.context().getDouble('light1Value', light1.getValue())) - regCoefLin = theAccessory.getDataHistory().regression(ETrendFunction.LIN, Math.ceil(UserDefinedProtocol.photosynthesisRateCurveEvalFraction * UserDefinedProtocol.respirationMeasurementDuration / UserDefinedProtocol.oxygenRapidMeasurementInterval)) - debugLogger('O2 resp. parameters: ' + regCoefLin.join(', ')) - rateO2Resp = theAccessory.context().get('rateO2Resp', []) - rateO2RespR2 = theAccessory.context().get('rateO2RespR2', []) - rateO2Resp[changeCounter] = round(regCoefLin[1] * 600, 2) - rateO2RespR2[changeCounter] = round(regCoefLin[2], 3) + if ((experimentDuration > (resumeTime - UserDefinedProtocol.respirationMeasurementDuration)) && !photosynthesis) { + theAccessory.context().put('photosynthesis', 1) + if (UserDefinedProtocol.respirationMeasurementDuration > 0) { + light0.suspend(resumeTime) + light1.suspend(resumeTime) + } + regCoefLin = theAccessory.getDataHistory().regression(ETrendFunction.LIN, Math.ceil(UserDefinedProtocol.photosynthesisRateCurveEvalFraction * UserDefinedProtocol.oxygenMeasurementDuration / UserDefinedProtocol.oxygenRapidMeasurementInterval)) + debugLogger('O2 evol. parameters: ' + regCoefLin.join(', ')) + rateO2Evol = theAccessory.context().get('rateO2Evol', []) + rateO2EvolR2 = theAccessory.context().get('rateO2EvolR2', []) + rateO2Evol[changeCounter] = round(regCoefLin[1] * 600, 2) + rateO2EvolR2[changeCounter] = round(regCoefLin[2], 3) // TODO should be function1 } - } - if (experimentDuration > (resumeTime + UserDefinedProtocol.relaxationPhaseDuration)) { - theAccessory.context().put('bubblingSuspended', 0) - theAccessory.context().put('photosynthesis', 0) - theAccessory.context().put('respiration', 0) - theAccessory.context().put('changeCounter', ++changeCounter) - if (changeCounter >= UserDefinedProtocol.lightStepMultiplierValues.length) { - theAccessory.context().put('changeCounter', 0) - theAccessory.context().put('measurementTime', experimentDuration + UserDefinedProtocol.photosynthesisMeasurementPeriod - UserDefinedProtocol.lightStepMultiplierValues.length * (UserDefinedProtocol.oxygenMeasurementDuration + UserDefinedProtocol.respirationMeasurementDuration + UserDefinedProtocol.relaxationPhaseDuration)) + if ((experimentDuration > resumeTime) && !respiration) { + theAccessory.context().put('respiration', 1) bubbles.setRunningProtoConfig(ProtoConfig.ON) stirrer.setRunningProtoConfig(new ProtoConfig(UserDefinedProtocol.stirrerIntensityValues[0])) - theAccessory.context().put('modeO2EvolResp', 0) - multiplierStep = multiplierStep < (UserDefinedProtocol.photosynthesisCurveLightMultiplierValues.length - 1) ? ++multiplierStep : 0 - theAccessory.context().put('multiplierStep', multiplierStep) - rateO2Evol = theAccessory.context().get('rateO2Evol', []) - rateO2EvolR2 = theAccessory.context().get('rateO2EvolR2', []) - rateO2Resp = theAccessory.context().get('rateO2Resp', []) - rateO2RespR2 = theAccessory.context().get('rateO2RespR2', []) - theAccessory.context().put('rateO2Evol', []) - theAccessory.context().put('rateO2EvolR2', []) - theAccessory.context().put('rateO2Resp', []) - theAccessory.context().put('rateO2RespR2', []) - theExperiment.addEvent('PI-curve DONE. O2 rates are ' + rateO2Evol.join(', ') + ' and ' + rateO2Resp.join(', ') + ' units/min (R2 ' + rateO2EvolR2.join(', ') + ' and ' + rateO2RespR2.join(', ') + ')') - debugLogger('PI-curve finished.') + if (UserDefinedProtocol.respirationMeasurementDuration > 0) { + light0.resume(experimentDuration) + light1.resume(experimentDuration) + controlLights(theAccessory.context().getDouble('light0Value', light0.getValue()), theAccessory.context().getDouble('light1Value', light1.getValue())) + regCoefLin = theAccessory.getDataHistory().regression(ETrendFunction.LIN, Math.ceil(UserDefinedProtocol.photosynthesisRateCurveEvalFraction * UserDefinedProtocol.respirationMeasurementDuration / UserDefinedProtocol.oxygenRapidMeasurementInterval)) + debugLogger('O2 resp. parameters: ' + regCoefLin.join(', ')) + rateO2Resp = theAccessory.context().get('rateO2Resp', []) + rateO2RespR2 = theAccessory.context().get('rateO2RespR2', []) + rateO2Resp[changeCounter] = round(regCoefLin[1] * 600, 2) + rateO2RespR2[changeCounter] = round(regCoefLin[2], 3) + // TODO should be function1 + } + } + if (experimentDuration > (resumeTime + UserDefinedProtocol.relaxationPhaseDuration)) { + theAccessory.context().put('bubblingSuspended', 0) + theAccessory.context().put('photosynthesis', 0) + theAccessory.context().put('respiration', 0) + theAccessory.context().put('changeCounter', ++changeCounter) + if (changeCounter >= UserDefinedProtocol.lightStepMultiplierValues.length) { + theAccessory.context().put('changeCounter', 0) + theAccessory.context().put('measurementTime', experimentDuration + UserDefinedProtocol.photosynthesisMeasurementPeriod - UserDefinedProtocol.lightStepMultiplierValues.length * (UserDefinedProtocol.oxygenMeasurementDuration + UserDefinedProtocol.respirationMeasurementDuration + UserDefinedProtocol.relaxationPhaseDuration)) + bubbles.setRunningProtoConfig(ProtoConfig.ON) + stirrer.setRunningProtoConfig(new ProtoConfig(UserDefinedProtocol.stirrerIntensityValues[0])) + theAccessory.context().put('modeO2EvolResp', 0) + multiplierStep = multiplierStep < (UserDefinedProtocol.photosynthesisCurveLightMultiplierValues.length - 1) ? ++multiplierStep : 0 + theAccessory.context().put('multiplierStep', multiplierStep) + rateO2Evol = theAccessory.context().get('rateO2Evol', []) + rateO2EvolR2 = theAccessory.context().get('rateO2EvolR2', []) + rateO2Resp = theAccessory.context().get('rateO2Resp', []) + rateO2RespR2 = theAccessory.context().get('rateO2RespR2', []) + theAccessory.context().put('rateO2Evol', []) + theAccessory.context().put('rateO2EvolR2', []) + theAccessory.context().put('rateO2Resp', []) + theAccessory.context().put('rateO2RespR2', []) + theExperiment.addEvent('PI-CURVE done. O2 rates are ' + rateO2Evol.join(', ') + ' and ' + rateO2Resp.join(', ') + ' units/min (R2 ' + rateO2EvolR2.join(', ') + ' and ' + rateO2RespR2.join(', ') + ')') + } } + result = UserDefinedProtocol.oxygenRapidMeasurementInterval } - result = UserDefinedProtocol.oxygenRapidMeasurementInterval + } catch (error) { + debugLogger('O2/resp measurement ERROR. ' + error.name + ' : ' + error.message) } } else if (experimentDuration > theAccessory.context().getInt('checkupTime', 0)) { // Here comes a hack that solves an issue with strange periodic behaviour of both the bubble interrupting valve and the stirrer, when they turn off in uncontrolled manner - most likely bug in the software From de1ccc5c3a888bdfec7b6224ef0c10acfbec9d21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cerven=C3=BD?= Date: Thu, 19 Nov 2020 20:07:29 +0100 Subject: [PATCH 23/47] Fixed neighbours "scanning" and cognitionPart "self-awareness" --- PP-GrowthOptimizer.js | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/PP-GrowthOptimizer.js b/PP-GrowthOptimizer.js index d08c9d2..4f75cb7 100644 --- a/PP-GrowthOptimizer.js +++ b/PP-GrowthOptimizer.js @@ -564,9 +564,11 @@ function PSO (particleFitness) { var particleGlobalLearning = 1.6 var particleInertiaWeighting = 2 * 0.8 / (particleCognitionLearning + particleSocialLearning + particleGlobalLearning - 2) var temporaryNeighborPosition - var neighborsPosition = [] - var neighborsFitness = [] + var neighborsPosition + var neighborsFitness for (var index = 0, len = UserDefinedProtocol.controlledParameters.length; index < len; index++) { + neighborsPosition = [] + neighborsFitness = [] if (len > 1) { parametersSearchRange = UserDefinedProtocol.parametersSearchRange[index] } @@ -576,8 +578,6 @@ function PSO (particleFitness) { debugLogger('BioArInEO-PSO particle step for ' + UserDefinedProtocol.controlledParameters[index] + ' is ' + particleStep[index].toFixed(2) + ' (max ' + UserDefinedProtocol.parametersMaxStep[index].toFixed(2) + ' )') for (var indexN = 0, lenN = neighborsList.length; indexN < lenN; ++indexN) { temporaryNeighborPosition = theServer.getGroupByName(neighborsList[indexN]).getAccessory('pumps.pump-5').context().get('particleLastPosition', undefined) - neighborsPosition = [] - neighborsFitness = [] if (temporaryNeighborPosition === undefined) { neighborsPosition.push(particlePosition[index]) } else { @@ -587,9 +587,13 @@ function PSO (particleFitness) { } neighborsBestFitness.push(Math.min.apply(null, neighborsFitness)) neighborsBestPosition.push(Number(neighborsPosition[neighborsFitness.indexOf(neighborsBestFitness[index])])) - debugLogger('BioArInEO-PSO neighbors best position for ' + UserDefinedProtocol.controlledParameters[index] + ' is ' + neighborsBestPosition[index].toFixed(2) + ' with fitness ' + neighborsBestFitness[index].toFixed(2)) - newStep.push(particleInertiaWeighting * particleStep[index] + particleCognitionLearning * Math.random() * (particleBestPosition[index] - particlePosition[index]) + particleSocialLearning * Math.random() * (neighborsBestPosition[index] - particlePosition[index]) + particleGlobalLearning * Math.random() * (swarmBestPosition[index] - particlePosition[index])) - debugLogger('BioArInEO-PSO new uncorrected step for ' + UserDefinedProtocol.controlledParameters[index] + ' is ' + newStep[index].toFixed(2)) + //debugLogger('BioArInEO-PSO neighbors best position for ' + UserDefinedProtocol.controlledParameters[index] + ' is ' + neighborsBestPosition[index].toFixed(2) + ' with fitness ' + neighborsBestFitness[index].toFixed(2)) + // PSO steps for debugging + var cognitionPart = particleFitness > particleBestFitness ? particleCognitionLearning * Math.random() * (particleBestPosition[index] - particlePosition[index]) : 0 + var socialPart = particleSocialLearning * Math.random() * (neighborsBestPosition[index] - particlePosition[index]) + var globalPart = particleGlobalLearning * Math.random() * (swarmBestPosition[index] - particlePosition[index]) + newStep.push(particleInertiaWeighting * particleStep[index] + cognitionPart + socialPart + globalPart) + //debugLogger('BioArInEO-PSO new uncorrected step for ' + UserDefinedProtocol.controlledParameters[index] + ' is ' + newStep[index].toFixed(2) + 'with [ ' + Array(cognitionPart,socialPart,globalPart).toString() + ' ]') if (Math.abs(newStep[index]) > Number(UserDefinedProtocol.parametersMaxStep[index])) { newStep[index] = Number(UserDefinedProtocol.parametersMaxStep[index]) * (newStep[index] > 0 ? 1 : -1) } From 7d7df9c03db98aa5c15a9715228c208d88867f66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cerven=C3=BD?= Date: Fri, 20 Nov 2020 09:17:55 +0100 Subject: [PATCH 24/47] Corrected swarm best properties logging --- PP-GrowthOptimizer.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/PP-GrowthOptimizer.js b/PP-GrowthOptimizer.js index 4f75cb7..fe3b118 100644 --- a/PP-GrowthOptimizer.js +++ b/PP-GrowthOptimizer.js @@ -575,7 +575,7 @@ function PSO (particleFitness) { if (temporaryTest) { particleStep.push(0.2 * (parametersSearchRange[1] - parametersSearchRange[0]) * (getRandomOnInterval(-1, 1) > 0 ? 1 : -1)) // ! PSI PBR JS doesn't support Math.sign() } - debugLogger('BioArInEO-PSO particle step for ' + UserDefinedProtocol.controlledParameters[index] + ' is ' + particleStep[index].toFixed(2) + ' (max ' + UserDefinedProtocol.parametersMaxStep[index].toFixed(2) + ' )') + debugLogger('BioArInEO-PSO particle step for ' + UserDefinedProtocol.controlledParameters[index] + ' was ' + particleStep[index].toFixed(2) + ' (|max| ' + UserDefinedProtocol.parametersMaxStep[index].toFixed(2) + ' )') for (var indexN = 0, lenN = neighborsList.length; indexN < lenN; ++indexN) { temporaryNeighborPosition = theServer.getGroupByName(neighborsList[indexN]).getAccessory('pumps.pump-5').context().get('particleLastPosition', undefined) if (temporaryNeighborPosition === undefined) { @@ -607,11 +607,12 @@ function PSO (particleFitness) { controlParameter(UserDefinedProtocol.controlledParameters[index], round(newPosition[index], 2)) } if (!(particleFitness > swarmBestFitness)) { + swarmBestPosition = particlePosition + swarmBestFitness = particleFitness theAccessory.context().put('particleBestPosition', particlePosition) theAccessory.context().put('particleBestFitness', particleFitness) - swarmLeader.getAccessory('pumps.pump-5').context().put('swarmBestPosition', particlePosition) - swarmLeader.getAccessory('pumps.pump-5').context().put('swarmBestFitness', particleFitness) - swarmBestParticle = theGroup + swarmLeader.getAccessory('pumps.pump-5').context().put('swarmBestPosition', swarmBestPosition) + swarmLeader.getAccessory('pumps.pump-5').context().put('swarmBestFitness', swarmBestFitness) swarmLeader.getAccessory('pumps.pump-5').context().put('swarmBestParticle', swarmBestParticle) } else if (!(particleFitness > particleBestFitness)) { theAccessory.context().put('particleBestPosition', particlePosition) From 6efc4a0f55facec79b2e168821fd27d85bde0594 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cerven=C3=BD?= Date: Sat, 28 Nov 2020 13:26:03 +0100 Subject: [PATCH 25/47] Improved logging and email notifications --- PP-GrowthOptimizer.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/PP-GrowthOptimizer.js b/PP-GrowthOptimizer.js index fe3b118..4475658 100644 --- a/PP-GrowthOptimizer.js +++ b/PP-GrowthOptimizer.js @@ -45,8 +45,8 @@ var UserDefinedProtocol = { * @author CzechGlobe - Department of Adaptive Biotechnologies (JaCe) * @copyright Jan Červený 2020(c) * @license MIT - * @version 3.4.5 - * @modified 26.10.2020 (JaCe) + * @version 3.4.6 + * @modified 20.11.2020 (JaCe) * * @notes For proper functionality of the script "OD Regulator" protocol has to be disabled as well as chosen * controlled accessory protocols (i.e. Lights, Thermoregulation, GMS, Stirrer). @@ -497,7 +497,8 @@ function controlPump () { controlParameter(UserDefinedProtocol.controlledParameter, UserDefinedProtocol.controlledParameterSteps[1]) theAccessory.context().put('changeCounter', 1) } - theServer.sendMail('OPTIMIZER on ' + theGroup.getName() , 'NONE', ': for fitness ' + stepDoublingTimeAvg + ' set new position') // Email notifications + debugLogger('OPTIMIZER executed with fitness ' + stepDoublingTimeAvg.toFixed(2) + ' and position [ ' + UserDefinedProtocol.controlledParameterSteps[changeCounter].toFixed(2) + ' ]') + theServer.sendMail('OPTIMIZER on ' + theGroup.getName() , 'NONE', ': for fitness ' + stepDoublingTimeAvg + ' set new position [ ' + UserDefinedProtocol.controlledParameterSteps[changeCounter].toFixed(2) + ' ]') // Email notification } theAccessory.context().put('stabilizedTimeMax', theExperiment.getDurationSec() + UserDefinedProtocol.stabilizationTimeMax * 3600) theAccessory.context().remove('stepCounter') @@ -593,7 +594,7 @@ function PSO (particleFitness) { var socialPart = particleSocialLearning * Math.random() * (neighborsBestPosition[index] - particlePosition[index]) var globalPart = particleGlobalLearning * Math.random() * (swarmBestPosition[index] - particlePosition[index]) newStep.push(particleInertiaWeighting * particleStep[index] + cognitionPart + socialPart + globalPart) - //debugLogger('BioArInEO-PSO new uncorrected step for ' + UserDefinedProtocol.controlledParameters[index] + ' is ' + newStep[index].toFixed(2) + 'with [ ' + Array(cognitionPart,socialPart,globalPart).toString() + ' ]') + debugLogger('BioArInEO-PSO new uncorrected step for ' + UserDefinedProtocol.controlledParameters[index] + ' is ' + newStep[index].toFixed(2) + ' with [ ' + Array(cognitionPart.toFixed(2),socialPart.toFixed(2),globalPart.toFixed(2)).toString() + ' ]') if (Math.abs(newStep[index]) > Number(UserDefinedProtocol.parametersMaxStep[index])) { newStep[index] = Number(UserDefinedProtocol.parametersMaxStep[index]) * (newStep[index] > 0 ? 1 : -1) } @@ -609,6 +610,7 @@ function PSO (particleFitness) { if (!(particleFitness > swarmBestFitness)) { swarmBestPosition = particlePosition swarmBestFitness = particleFitness + swarmBestParticle = theGroup theAccessory.context().put('particleBestPosition', particlePosition) theAccessory.context().put('particleBestFitness', particleFitness) swarmLeader.getAccessory('pumps.pump-5').context().put('swarmBestPosition', swarmBestPosition) From 4a1f001071f77bef7affc1e07748ba9487dbd96c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cerven=C3=BD?= Date: Wed, 2 Dec 2020 13:57:19 +0100 Subject: [PATCH 26/47] Improved logging and email notifications --- PP-GrowthOptimizer.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/PP-GrowthOptimizer.js b/PP-GrowthOptimizer.js index 4475658..0470d95 100644 --- a/PP-GrowthOptimizer.js +++ b/PP-GrowthOptimizer.js @@ -45,8 +45,8 @@ var UserDefinedProtocol = { * @author CzechGlobe - Department of Adaptive Biotechnologies (JaCe) * @copyright Jan Červený 2020(c) * @license MIT - * @version 3.4.6 - * @modified 20.11.2020 (JaCe) + * @version 3.4.8 + * @modified 2.12.2020 (JaCe) * * @notes For proper functionality of the script "OD Regulator" protocol has to be disabled as well as chosen * controlled accessory protocols (i.e. Lights, Thermoregulation, GMS, Stirrer). @@ -497,7 +497,7 @@ function controlPump () { controlParameter(UserDefinedProtocol.controlledParameter, UserDefinedProtocol.controlledParameterSteps[1]) theAccessory.context().put('changeCounter', 1) } - debugLogger('OPTIMIZER executed with fitness ' + stepDoublingTimeAvg.toFixed(2) + ' and position [ ' + UserDefinedProtocol.controlledParameterSteps[changeCounter].toFixed(2) + ' ]') + debugLogger('OPTIMIZER executed with fitness ' + stepDoublingTimeAvg.toFixed(2) + ' and new position is [ ' + UserDefinedProtocol.controlledParameterSteps[changeCounter].toFixed(2) + ' ]') theServer.sendMail('OPTIMIZER on ' + theGroup.getName() , 'NONE', ': for fitness ' + stepDoublingTimeAvg + ' set new position [ ' + UserDefinedProtocol.controlledParameterSteps[changeCounter].toFixed(2) + ' ]') // Email notification } theAccessory.context().put('stabilizedTimeMax', theExperiment.getDurationSec() + UserDefinedProtocol.stabilizationTimeMax * 3600) @@ -590,7 +590,7 @@ function PSO (particleFitness) { neighborsBestPosition.push(Number(neighborsPosition[neighborsFitness.indexOf(neighborsBestFitness[index])])) //debugLogger('BioArInEO-PSO neighbors best position for ' + UserDefinedProtocol.controlledParameters[index] + ' is ' + neighborsBestPosition[index].toFixed(2) + ' with fitness ' + neighborsBestFitness[index].toFixed(2)) // PSO steps for debugging - var cognitionPart = particleFitness > particleBestFitness ? particleCognitionLearning * Math.random() * (particleBestPosition[index] - particlePosition[index]) : 0 + var cognitionPart = !(particleFitness < particleBestFitness) ? particleCognitionLearning * Math.random() * (particleBestPosition[index] - particlePosition[index]) : 0 var socialPart = particleSocialLearning * Math.random() * (neighborsBestPosition[index] - particlePosition[index]) var globalPart = particleGlobalLearning * Math.random() * (swarmBestPosition[index] - particlePosition[index]) newStep.push(particleInertiaWeighting * particleStep[index] + cognitionPart + socialPart + globalPart) From 8e0bfce31db01dd448f10891c1f3d809959c4d13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cerven=C3=BD?= Date: Thu, 3 Dec 2020 07:36:49 +0100 Subject: [PATCH 27/47] PSO new position setting improvement (when best of the best) --- PP-GrowthOptimizer.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/PP-GrowthOptimizer.js b/PP-GrowthOptimizer.js index 0470d95..b64d640 100644 --- a/PP-GrowthOptimizer.js +++ b/PP-GrowthOptimizer.js @@ -45,8 +45,8 @@ var UserDefinedProtocol = { * @author CzechGlobe - Department of Adaptive Biotechnologies (JaCe) * @copyright Jan Červený 2020(c) * @license MIT - * @version 3.4.8 - * @modified 2.12.2020 (JaCe) + * @version 3.4.9 + * @modified 3.12.2020 (JaCe) * * @notes For proper functionality of the script "OD Regulator" protocol has to be disabled as well as chosen * controlled accessory protocols (i.e. Lights, Thermoregulation, GMS, Stirrer). @@ -590,9 +590,9 @@ function PSO (particleFitness) { neighborsBestPosition.push(Number(neighborsPosition[neighborsFitness.indexOf(neighborsBestFitness[index])])) //debugLogger('BioArInEO-PSO neighbors best position for ' + UserDefinedProtocol.controlledParameters[index] + ' is ' + neighborsBestPosition[index].toFixed(2) + ' with fitness ' + neighborsBestFitness[index].toFixed(2)) // PSO steps for debugging - var cognitionPart = !(particleFitness < particleBestFitness) ? particleCognitionLearning * Math.random() * (particleBestPosition[index] - particlePosition[index]) : 0 - var socialPart = particleSocialLearning * Math.random() * (neighborsBestPosition[index] - particlePosition[index]) - var globalPart = particleGlobalLearning * Math.random() * (swarmBestPosition[index] - particlePosition[index]) + var cognitionPart = particleFitness > particleBestFitness ? particleCognitionLearning * Math.random() * (particleBestPosition[index] - particlePosition[index]) : 0 + var socialPart = particleFitness > neighborsBestFitness[index] ? particleSocialLearning * Math.random() * (neighborsBestPosition[index] - particlePosition[index]) : 0 + var globalPart = particleFitness > swarmBestFitness ? particleGlobalLearning * Math.random() * (swarmBestPosition[index] - particlePosition[index]) : 0 newStep.push(particleInertiaWeighting * particleStep[index] + cognitionPart + socialPart + globalPart) debugLogger('BioArInEO-PSO new uncorrected step for ' + UserDefinedProtocol.controlledParameters[index] + ' is ' + newStep[index].toFixed(2) + ' with [ ' + Array(cognitionPart.toFixed(2),socialPart.toFixed(2),globalPart.toFixed(2)).toString() + ' ]') if (Math.abs(newStep[index]) > Number(UserDefinedProtocol.parametersMaxStep[index])) { From 32a225d67935ea5d9f563c28e51b9f283896a5d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cerven=C3=BD?= Date: Fri, 4 Dec 2020 10:42:28 +0100 Subject: [PATCH 28/47] Changed behavior of stabilizedMin/Max check --- PP-GrowthOptimizer.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/PP-GrowthOptimizer.js b/PP-GrowthOptimizer.js index b64d640..406d98a 100644 --- a/PP-GrowthOptimizer.js +++ b/PP-GrowthOptimizer.js @@ -45,8 +45,8 @@ var UserDefinedProtocol = { * @author CzechGlobe - Department of Adaptive Biotechnologies (JaCe) * @copyright Jan Červený 2020(c) * @license MIT - * @version 3.4.9 - * @modified 3.12.2020 (JaCe) + * @version 3.5.0 + * @modified 4.12.2020 (JaCe) * * @notes For proper functionality of the script "OD Regulator" protocol has to be disabled as well as chosen * controlled accessory protocols (i.e. Lights, Thermoregulation, GMS, Stirrer). @@ -430,8 +430,8 @@ function controlPump () { theAccessory.context().put('expDuration', expDuration) theAccessory.context().put('stepDuration', stepDuration) theAccessory.context().put('stepDoublingTime', stepDoublingTime) - theAccessory.context().put('stabilizedTime', theExperiment.getDurationSec() + UserDefinedProtocol.stabilizationTimeMin * 3600) - theAccessory.context().put('stabilizedTimeMax', theExperiment.getDurationSec() + UserDefinedProtocol.stabilizationTimeMax * 3600) + // theAccessory.context().put('stabilizedTime', theExperiment.getDurationSec() + UserDefinedProtocol.stabilizationTimeMin * 3600) + // theAccessory.context().put('stabilizedTimeMax', theExperiment.getDurationSec() + UserDefinedProtocol.stabilizationTimeMax * 3600) odSensorRegression.getDataHistory().setCapacity(600) } expDuration[stepCounter] = theExperiment.getDurationSec() @@ -500,6 +500,7 @@ function controlPump () { debugLogger('OPTIMIZER executed with fitness ' + stepDoublingTimeAvg.toFixed(2) + ' and new position is [ ' + UserDefinedProtocol.controlledParameterSteps[changeCounter].toFixed(2) + ' ]') theServer.sendMail('OPTIMIZER on ' + theGroup.getName() , 'NONE', ': for fitness ' + stepDoublingTimeAvg + ' set new position [ ' + UserDefinedProtocol.controlledParameterSteps[changeCounter].toFixed(2) + ' ]') // Email notification } + theAccessory.context().put('stabilizedTime', theExperiment.getDurationSec() + UserDefinedProtocol.stabilizationTimeMin * 3600) theAccessory.context().put('stabilizedTimeMax', theExperiment.getDurationSec() + UserDefinedProtocol.stabilizationTimeMax * 3600) theAccessory.context().remove('stepCounter') theAccessory.context().remove('expDuration') From 075767123ebf80437ca7cd2ae8d8f1aaea2080b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cerven=C3=BD?= Date: Sun, 20 Dec 2020 09:28:18 +0100 Subject: [PATCH 29/47] Improved changing of multistep parameter (fun) --- PP-GrowthOptimizer.js | 67 +++++++++++++++++++++++++------------------ 1 file changed, 39 insertions(+), 28 deletions(-) diff --git a/PP-GrowthOptimizer.js b/PP-GrowthOptimizer.js index 406d98a..537abbe 100644 --- a/PP-GrowthOptimizer.js +++ b/PP-GrowthOptimizer.js @@ -45,8 +45,8 @@ var UserDefinedProtocol = { * @author CzechGlobe - Department of Adaptive Biotechnologies (JaCe) * @copyright Jan Červený 2020(c) * @license MIT - * @version 3.5.0 - * @modified 4.12.2020 (JaCe) + * @version 3.5.1 + * @modified 19.12.2020 (JaCe) * * @notes For proper functionality of the script "OD Regulator" protocol has to be disabled as well as chosen * controlled accessory protocols (i.e. Lights, Thermoregulation, GMS, Stirrer). @@ -336,6 +336,38 @@ function controlParameter (parameter, values) { theAccessory.context().put('controlledParameterText', parameter + ' ' + (Array.isArray(values) ? values.join(' and ') : values) + unit) theExperiment.addEvent(parameter[0].toUpperCase() + parameter.slice(1) + ' changed to ' + (Array.isArray(values) ? values.join(' and ') : values) + unit) } +function changeParameter(parameter,direction) { + if ((parameter === undefined) || (parameter === 'none')) { + return null + } + if(direction === undefined) { + direction = theAccessory.context().get('controlledParameterDirection', 'next') + } + var controlledParameterPosition = theAccessory.context().getInt('controlledParameterPosition', 1) + var positions = [] + positions.push(UserDefinedProtocol.controlledParameterSteps[controlledParameterPosition-1]) + var len = UserDefinedProtocol.controlledParameterSteps.length + if(direction === 'reverse') { + direction = (theAccessory.context().get('controlledParameterDirection', 'next') === 'previous') ? 'next' : 'previous' + } + switch(direction) { + case 'next': + if(++controlledParameterPosition > len) { + theAccessory.context().put('controlledParameterDirection', 'previous') + controlledParameterPosition = controlledParameterPosition - 2 + } + break; + case 'previous': + if(--controlledParameterPosition < 1) { + theAccessory.context().put('controlledParameterDirection', 'next') + controlledParameterPosition = controlledParameterPosition + 2 + } + break; + default: + } + controlParameter(parameter, UserDefinedProtocol.controlledParameterSteps[controlledParameterPosition-1]) + return positions.push(UserDefinedProtocol.controlledParameterSteps[controlledParameterPosition-1]) +} // Control activity of the peristaltic pump function controlPump () { // Following code ready for functional implementation @@ -398,16 +430,7 @@ function controlPump () { var stepDoublingTimeAvg = len > 2 ? len > UserDefinedProtocol.analyzedSteps ? stepDoublingTime.slice(len - UserDefinedProtocol.analyzedSteps, len).reduce(getSumArrReduce, 0) / (UserDefinedProtocol.analyzedSteps) : stepDoublingTime.slice(1, len).reduce(getSumArrReduce, 0) / (len - 1) : stepDoublingTime[len - 1] PSO(stepDoublingTimeAvg) } else if (UserDefinedProtocol.controlledParameterSteps.length > 1) { - if (changeCounter < (UserDefinedProtocol.controlledParameterSteps.length - 1)) { - controlParameter(UserDefinedProtocol.controlledParameter, UserDefinedProtocol.controlledParameterSteps[++changeCounter]) - theAccessory.context().put('changeCounter', changeCounter) - } else if (changeCounter < 2 * (UserDefinedProtocol.controlledParameterSteps.length - 1)) { - controlParameter(UserDefinedProtocol.controlledParameter, UserDefinedProtocol.controlledParameterSteps[2 * (UserDefinedProtocol.controlledParameterSteps.length - 1) - (++changeCounter)]) - theAccessory.context().put('changeCounter', changeCounter) - } else { - controlParameter(UserDefinedProtocol.controlledParameter, UserDefinedProtocol.controlledParameterSteps[1]) - theAccessory.context().put('changeCounter', 1) - } + var position = changeParameter(UserDefinedProtocol.controlledParameter) } theAccessory.context().remove('stepCounter') theAccessory.context().remove('expDuration') @@ -418,12 +441,10 @@ function controlPump () { if (((odValue > (UserDefinedProtocol.turbidostatODMax * odMaxModifier)) && !pumpState)) { theAccessory.context().put('modeDilution', 1) theAccessory.context().put('modeStabilized', 0) - // var stepCounter = theAccessory.context().getInt('stepCounter', 0) var expDuration = theAccessory.context().get('expDuration', 0.0) var stepDuration = theAccessory.context().get('stepDuration', 0.0) var stepDoublingTime = theAccessory.context().get('stepDoublingTime', [ 999.9 ]) var stabilizedTime = theAccessory.context().getInt('stabilizedTime', 0) - // var stabilizedTimeMax = theAccessory.context().getInt('stabilizedTimeMax', 0) if (!Array.isArray(expDuration)) { stepCounter = 0 expDuration = []; stepDuration = []; stepDoublingTime = [] @@ -482,23 +503,13 @@ function controlPump () { // Growth stability test and parameters control if (((stepDoublingTimeIC95 / stepDoublingTimeAvg) <= (UserDefinedProtocol.intervalOfConfidenceMax / 100) && (Math.abs(stepTrend / stepDoublingTimeAvg) <= (UserDefinedProtocol.growthTrendMax / 100)) && (stabilizedTime <= Number(theExperiment.getDurationSec())))) { theAccessory.context().put('modeStabilized', 1) - // changeCounter = theAccessory.context().getInt('changeCounter', 0) theExperiment.addEvent('*** Stabilized doubling time Dt (' + theGroup.getAccessory('thermo.thermo-reg').getValue() + ' ' + String.fromCharCode(176) + 'C, ' + theAccessory.context().getString('controlledParameterText', 'no parameter') + ') is ' + round(stepDoublingTimeAvg, 2) + String.fromCharCode(177) + round(stepDoublingTimeIC95, 2) + ' h (IC95)') if (UserDefinedProtocol.particleSwarmOptimizer) { PSO(stepDoublingTimeAvg) } else if (UserDefinedProtocol.controlledParameterSteps.length > 1) { - if (changeCounter < (UserDefinedProtocol.controlledParameterSteps.length - 1)) { - controlParameter(UserDefinedProtocol.controlledParameter, UserDefinedProtocol.controlledParameterSteps[++changeCounter]) - theAccessory.context().put('changeCounter', changeCounter) - } else if (changeCounter < 2 * (UserDefinedProtocol.controlledParameterSteps.length - 1)) { - controlParameter(UserDefinedProtocol.controlledParameter, UserDefinedProtocol.controlledParameterSteps[2 * (UserDefinedProtocol.controlledParameterSteps.length - 1) - (++changeCounter)]) - theAccessory.context().put('changeCounter', changeCounter) - } else { - controlParameter(UserDefinedProtocol.controlledParameter, UserDefinedProtocol.controlledParameterSteps[1]) - theAccessory.context().put('changeCounter', 1) - } - debugLogger('OPTIMIZER executed with fitness ' + stepDoublingTimeAvg.toFixed(2) + ' and new position is [ ' + UserDefinedProtocol.controlledParameterSteps[changeCounter].toFixed(2) + ' ]') - theServer.sendMail('OPTIMIZER on ' + theGroup.getName() , 'NONE', ': for fitness ' + stepDoublingTimeAvg + ' set new position [ ' + UserDefinedProtocol.controlledParameterSteps[changeCounter].toFixed(2) + ' ]') // Email notification + var positions = changeParameter(UserDefinedProtocol.controlledParameter) + debugLogger('OPTIMIZER executed with fitness ' + stepDoublingTimeAvg.toFixed(2) + ' for ' + positions[0].toFixed(2) + ' and new position is [ ' + positions[1].toFixed(2) + ' ]') + theServer.sendMail('OPTIMIZER on ' + theGroup.getName() , 'NONE', ': for fitness ' + stepDoublingTimeAvg + ' set new position [ ' + UserDefinedProtocol.controlledParameterSteps[position - 1].toFixed(2) + ' ]') // Email notification } theAccessory.context().put('stabilizedTime', theExperiment.getDurationSec() + UserDefinedProtocol.stabilizationTimeMin * 3600) theAccessory.context().put('stabilizedTimeMax', theExperiment.getDurationSec() + UserDefinedProtocol.stabilizationTimeMax * 3600) @@ -537,7 +548,7 @@ function PSO (particleFitness) { * @return {array} New parameters / conditions. */ if (particleFitness === undefined) { - particleFitness = [ 999.9 ] + particleFitness = 999.9 } theAccessory.context().put('particleLastFitness', particleFitness) var particlePosition = theAccessory.context().get('particlePosition', UserDefinedProtocol.controlledParametersIC) From e101430fff443e616faf4229e855da0ed94d5beb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cerven=C3=BD?= Date: Sun, 20 Dec 2020 09:32:40 +0100 Subject: [PATCH 30/47] Improved changing of multistep parameter (fun) --- PP-GrowthOptimizer.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/PP-GrowthOptimizer.js b/PP-GrowthOptimizer.js index 537abbe..b07f04f 100644 --- a/PP-GrowthOptimizer.js +++ b/PP-GrowthOptimizer.js @@ -366,7 +366,8 @@ function changeParameter(parameter,direction) { default: } controlParameter(parameter, UserDefinedProtocol.controlledParameterSteps[controlledParameterPosition-1]) - return positions.push(UserDefinedProtocol.controlledParameterSteps[controlledParameterPosition-1]) + positions.push(UserDefinedProtocol.controlledParameterSteps[controlledParameterPosition-1]) + return positions } // Control activity of the peristaltic pump function controlPump () { @@ -508,8 +509,8 @@ function controlPump () { PSO(stepDoublingTimeAvg) } else if (UserDefinedProtocol.controlledParameterSteps.length > 1) { var positions = changeParameter(UserDefinedProtocol.controlledParameter) - debugLogger('OPTIMIZER executed with fitness ' + stepDoublingTimeAvg.toFixed(2) + ' for ' + positions[0].toFixed(2) + ' and new position is [ ' + positions[1].toFixed(2) + ' ]') - theServer.sendMail('OPTIMIZER on ' + theGroup.getName() , 'NONE', ': for fitness ' + stepDoublingTimeAvg + ' set new position [ ' + UserDefinedProtocol.controlledParameterSteps[position - 1].toFixed(2) + ' ]') // Email notification + debugLogger('OPTIMIZER executed with fitness ' + stepDoublingTimeAvg.toFixed(2) + ' for [ ' + positions[0].toFixed(2) + ' ] and new position is [ ' + positions[1].toFixed(2) + ' ]') + theServer.sendMail('OPTIMIZER on ' + theGroup.getName() , 'NONE', ': for fitness ' + stepDoublingTimeAvg + ' at [ ' + positions[0].toFixed(2) + ' ] set new position [ ' + positions[1].toFixed(2) + ' ]') // Email notification } theAccessory.context().put('stabilizedTime', theExperiment.getDurationSec() + UserDefinedProtocol.stabilizationTimeMin * 3600) theAccessory.context().put('stabilizedTimeMax', theExperiment.getDurationSec() + UserDefinedProtocol.stabilizationTimeMax * 3600) From d1328cf814205b2e2c1d0818f056cc2aefc8c03d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cerven=C3=BD?= Date: Sun, 20 Dec 2020 19:28:11 +0100 Subject: [PATCH 31/47] parameterChange() bug fixes --- PP-GrowthOptimizer.js | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/PP-GrowthOptimizer.js b/PP-GrowthOptimizer.js index b07f04f..3bdd8b7 100644 --- a/PP-GrowthOptimizer.js +++ b/PP-GrowthOptimizer.js @@ -336,7 +336,7 @@ function controlParameter (parameter, values) { theAccessory.context().put('controlledParameterText', parameter + ' ' + (Array.isArray(values) ? values.join(' and ') : values) + unit) theExperiment.addEvent(parameter[0].toUpperCase() + parameter.slice(1) + ' changed to ' + (Array.isArray(values) ? values.join(' and ') : values) + unit) } -function changeParameter(parameter,direction) { +function changeParameter(parameter, direction) { if ((parameter === undefined) || (parameter === 'none')) { return null } @@ -365,6 +365,7 @@ function changeParameter(parameter,direction) { break; default: } + theAccessory.context().put('controlledParameterPosition', controlledParameterPosition) controlParameter(parameter, UserDefinedProtocol.controlledParameterSteps[controlledParameterPosition-1]) positions.push(UserDefinedProtocol.controlledParameterSteps[controlledParameterPosition-1]) return positions @@ -399,7 +400,6 @@ function controlPump () { var odNoise = theAccessory.context().getInt('odNoise', 1) var odMinModifier = theAccessory.context().getDouble('odMinModifier', 1.0) var odMaxModifier = theAccessory.context().getDouble('odMaxModifier', 1.0) - var changeCounter = theAccessory.context().getInt('changeCounter', 0) var stepCounter = theAccessory.context().getInt('stepCounter', 0) // Check for OD noise/overshots and primitive OD averaging if (!isNaN(odValue) && (round(odValue, 3) !== round(odLast, 3))) { @@ -424,19 +424,22 @@ function controlPump () { UserDefinedProtocol.turbidostatODMin = (UserDefinedProtocol.turbidostatODMax - UserDefinedProtocol.turbidostatODMin) + (UserDefinedProtocol.turbidostatODMax = UserDefinedProtocol.turbidostatODMin) } if (theAccessory.context().getInt('stabilizedTimeMax', 0) <= Number(theExperiment.getDurationSec()) && !pumpState) { - theAccessory.context().put('stabilizedTimeMax', theExperiment.getDurationSec() + UserDefinedProtocol.stabilizationTimeMax * 3600) // TODO allocate var for getDurationSec + // TODO allocate var for getDurationSec var stepDoublingTime = theAccessory.context().get('stepDoublingTime', [ 999.9 ]) if (UserDefinedProtocol.particleSwarmOptimizer) { var len = stepDoublingTime.length var stepDoublingTimeAvg = len > 2 ? len > UserDefinedProtocol.analyzedSteps ? stepDoublingTime.slice(len - UserDefinedProtocol.analyzedSteps, len).reduce(getSumArrReduce, 0) / (UserDefinedProtocol.analyzedSteps) : stepDoublingTime.slice(1, len).reduce(getSumArrReduce, 0) / (len - 1) : stepDoublingTime[len - 1] PSO(stepDoublingTimeAvg) } else if (UserDefinedProtocol.controlledParameterSteps.length > 1) { - var position = changeParameter(UserDefinedProtocol.controlledParameter) + var positions = changeParameter(UserDefinedProtocol.controlledParameter) + debugLogger('OPTIMIZER executed on max. time with fitness ' + stepDoublingTimeAvg.toFixed(2) + ' for [ ' + positions[0].toFixed(2) + ' ] and new position is [ ' + positions[1].toFixed(2) + ' ]') + theServer.sendMail('OPTIMIZER (max. time) on ' + theGroup.getName() , 'NONE', ': fitness ' + stepDoublingTimeAvg.toFixed(2) + ' for [ ' + positions[0].toFixed(2) + ' ] and set new position [ ' + positions[1].toFixed(2) + ' ]') // Email notification } + theAccessory.context().put('stabilizedTime', theExperiment.getDurationSec() + UserDefinedProtocol.stabilizationTimeMin * 3600) + theAccessory.context().put('stabilizedTimeMax', theExperiment.getDurationSec() + UserDefinedProtocol.stabilizationTimeMax * 3600) theAccessory.context().remove('stepCounter') theAccessory.context().remove('expDuration') theAccessory.context().remove('stepDoublingTime') - theAccessory.context().remove('stabilizedTime') } // Start step growth rate evaluation if (((odValue > (UserDefinedProtocol.turbidostatODMax * odMaxModifier)) && !pumpState)) { @@ -445,15 +448,12 @@ function controlPump () { var expDuration = theAccessory.context().get('expDuration', 0.0) var stepDuration = theAccessory.context().get('stepDuration', 0.0) var stepDoublingTime = theAccessory.context().get('stepDoublingTime', [ 999.9 ]) - var stabilizedTime = theAccessory.context().getInt('stabilizedTime', 0) if (!Array.isArray(expDuration)) { stepCounter = 0 expDuration = []; stepDuration = []; stepDoublingTime = [] theAccessory.context().put('expDuration', expDuration) theAccessory.context().put('stepDuration', stepDuration) theAccessory.context().put('stepDoublingTime', stepDoublingTime) - // theAccessory.context().put('stabilizedTime', theExperiment.getDurationSec() + UserDefinedProtocol.stabilizationTimeMin * 3600) - // theAccessory.context().put('stabilizedTimeMax', theExperiment.getDurationSec() + UserDefinedProtocol.stabilizationTimeMax * 3600) odSensorRegression.getDataHistory().setCapacity(600) } expDuration[stepCounter] = theExperiment.getDurationSec() @@ -472,7 +472,6 @@ function controlPump () { var stepDoublingTimeSD = 0 var stepDoublingTimeIC95 = 0 var stepTrend = 0 - // var stepCoD = 0 var sumXY = 0 var sumX = 0 var sumY = 0 @@ -499,7 +498,6 @@ function controlPump () { sumXY += Number(expDuration[i]) * Number(stepDoublingTime[i]) } stepTrend = (UserDefinedProtocol.analyzedSteps * sumXY - sumX * sumY) / (UserDefinedProtocol.analyzedSteps * sumX2 - Math.pow(sumX, 2)) * 3600 - // stepCoD = (UserDefinedProtocol.analyzedSteps * sumXY - sumX * sumY) / (Math.sqrt((UserDefinedProtocol.analyzedSteps * sumX2 - Math.pow(sumX, 2)) * (UserDefinedProtocol.analyzedSteps * sumY2 - Math.pow(sumY, 2)))) theExperiment.addEvent('Steps doubling time Avg: ' + round(stepDoublingTimeAvg, 2) + ' h, IC95 ' + round(stepDoublingTimeIC95, 2) + ' h (' + round(stepDoublingTimeIC95 / stepDoublingTimeAvg * 100, 1) + '%) with ' + round(stepTrend, 2) + ' h/h trend (' + round(stepTrend / stepDoublingTimeAvg * 100, 1) + '%)') // Growth stability test and parameters control if (((stepDoublingTimeIC95 / stepDoublingTimeAvg) <= (UserDefinedProtocol.intervalOfConfidenceMax / 100) && (Math.abs(stepTrend / stepDoublingTimeAvg) <= (UserDefinedProtocol.growthTrendMax / 100)) && (stabilizedTime <= Number(theExperiment.getDurationSec())))) { @@ -510,14 +508,13 @@ function controlPump () { } else if (UserDefinedProtocol.controlledParameterSteps.length > 1) { var positions = changeParameter(UserDefinedProtocol.controlledParameter) debugLogger('OPTIMIZER executed with fitness ' + stepDoublingTimeAvg.toFixed(2) + ' for [ ' + positions[0].toFixed(2) + ' ] and new position is [ ' + positions[1].toFixed(2) + ' ]') - theServer.sendMail('OPTIMIZER on ' + theGroup.getName() , 'NONE', ': for fitness ' + stepDoublingTimeAvg + ' at [ ' + positions[0].toFixed(2) + ' ] set new position [ ' + positions[1].toFixed(2) + ' ]') // Email notification + theServer.sendMail('OPTIMIZER on ' + theGroup.getName() , 'NONE', ': fitness ' + stepDoublingTimeAvg.toFixed(2) + ' for [ ' + positions[0].toFixed(2) + ' ] and set new position [ ' + positions[1].toFixed(2) + ' ]') // Email notification } theAccessory.context().put('stabilizedTime', theExperiment.getDurationSec() + UserDefinedProtocol.stabilizationTimeMin * 3600) theAccessory.context().put('stabilizedTimeMax', theExperiment.getDurationSec() + UserDefinedProtocol.stabilizationTimeMax * 3600) theAccessory.context().remove('stepCounter') theAccessory.context().remove('expDuration') theAccessory.context().remove('stepDoublingTime') - theAccessory.context().remove('stabilizedTime') } } } From 68e3a2ebf48903e6e5619c479fa88e43a9b891ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cerven=C3=BD?= Date: Sun, 20 Dec 2020 19:38:37 +0100 Subject: [PATCH 32/47] Improved experiment duration var handling --- PP-GrowthOptimizer.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/PP-GrowthOptimizer.js b/PP-GrowthOptimizer.js index 3bdd8b7..1a8132f 100644 --- a/PP-GrowthOptimizer.js +++ b/PP-GrowthOptimizer.js @@ -99,8 +99,8 @@ importPackage(Packages.psi.bioreactor.core.regression) if (!theAccessory.context().getInt('initiated', 0)) { try { theAccessory.context().clear() - theAccessory.context().put('stabilizedTime', theExperiment.getDurationSec() + UserDefinedProtocol.stabilizationTimeMin * 3600) - theAccessory.context().put('stabilizedTimeMax', theExperiment.getDurationSec() + UserDefinedProtocol.stabilizationTimeMax * 3600) + theAccessory.context().put('stabilizedTime', Number(theExperiment.getDurationSec()) + UserDefinedProtocol.stabilizationTimeMin * 3600) + theAccessory.context().put('stabilizedTimeMax', Number(theExperiment.getDurationSec()) + UserDefinedProtocol.stabilizationTimeMax * 3600) var light1String if (theGroup.getAccessory('actinic-lights.light-Blue') === null) { light1String = 'actinic-lights.light-White' @@ -376,6 +376,7 @@ function controlPump () { // setODSensorString("turbidostat"); // setODSensorString("regression"); var odSensorString, odSensorRegressionString + var experimentDuration = Number(theExperiment.getDurationSec()) switch (UserDefinedProtocol.turbidostatODType) { case 680: odSensorString = 'od-sensors.od-680' @@ -423,8 +424,7 @@ function controlPump () { if (UserDefinedProtocol.turbidostatODMin > UserDefinedProtocol.turbidostatODMax) { UserDefinedProtocol.turbidostatODMin = (UserDefinedProtocol.turbidostatODMax - UserDefinedProtocol.turbidostatODMin) + (UserDefinedProtocol.turbidostatODMax = UserDefinedProtocol.turbidostatODMin) } - if (theAccessory.context().getInt('stabilizedTimeMax', 0) <= Number(theExperiment.getDurationSec()) && !pumpState) { - // TODO allocate var for getDurationSec + if (theAccessory.context().getInt('stabilizedTimeMax', 0) <= experimentDuration && !pumpState) { var stepDoublingTime = theAccessory.context().get('stepDoublingTime', [ 999.9 ]) if (UserDefinedProtocol.particleSwarmOptimizer) { var len = stepDoublingTime.length @@ -435,8 +435,8 @@ function controlPump () { debugLogger('OPTIMIZER executed on max. time with fitness ' + stepDoublingTimeAvg.toFixed(2) + ' for [ ' + positions[0].toFixed(2) + ' ] and new position is [ ' + positions[1].toFixed(2) + ' ]') theServer.sendMail('OPTIMIZER (max. time) on ' + theGroup.getName() , 'NONE', ': fitness ' + stepDoublingTimeAvg.toFixed(2) + ' for [ ' + positions[0].toFixed(2) + ' ] and set new position [ ' + positions[1].toFixed(2) + ' ]') // Email notification } - theAccessory.context().put('stabilizedTime', theExperiment.getDurationSec() + UserDefinedProtocol.stabilizationTimeMin * 3600) - theAccessory.context().put('stabilizedTimeMax', theExperiment.getDurationSec() + UserDefinedProtocol.stabilizationTimeMax * 3600) + theAccessory.context().put('stabilizedTime', experimentDuration + UserDefinedProtocol.stabilizationTimeMin * 3600) + theAccessory.context().put('stabilizedTimeMax', experimentDuration + UserDefinedProtocol.stabilizationTimeMax * 3600) theAccessory.context().remove('stepCounter') theAccessory.context().remove('expDuration') theAccessory.context().remove('stepDoublingTime') @@ -456,7 +456,7 @@ function controlPump () { theAccessory.context().put('stepDoublingTime', stepDoublingTime) odSensorRegression.getDataHistory().setCapacity(600) } - expDuration[stepCounter] = theExperiment.getDurationSec() + expDuration[stepCounter] = experimentDuration stepDuration[stepCounter] = expDuration[stepCounter] - theAccessory.context().getInt('lastPumpStop', expDuration[stepCounter]) if ((stepDuration[stepCounter] > 0) && UserDefinedProtocol.growthStatistics) { var DHCapacity = (Math.floor(stepDuration[stepCounter] / UserDefinedProtocol.ODReadoutInterval) - 3) > 0 ? (Math.floor(stepDuration[stepCounter] / UserDefinedProtocol.ODReadoutInterval) - 3) : 60 @@ -500,7 +500,7 @@ function controlPump () { stepTrend = (UserDefinedProtocol.analyzedSteps * sumXY - sumX * sumY) / (UserDefinedProtocol.analyzedSteps * sumX2 - Math.pow(sumX, 2)) * 3600 theExperiment.addEvent('Steps doubling time Avg: ' + round(stepDoublingTimeAvg, 2) + ' h, IC95 ' + round(stepDoublingTimeIC95, 2) + ' h (' + round(stepDoublingTimeIC95 / stepDoublingTimeAvg * 100, 1) + '%) with ' + round(stepTrend, 2) + ' h/h trend (' + round(stepTrend / stepDoublingTimeAvg * 100, 1) + '%)') // Growth stability test and parameters control - if (((stepDoublingTimeIC95 / stepDoublingTimeAvg) <= (UserDefinedProtocol.intervalOfConfidenceMax / 100) && (Math.abs(stepTrend / stepDoublingTimeAvg) <= (UserDefinedProtocol.growthTrendMax / 100)) && (stabilizedTime <= Number(theExperiment.getDurationSec())))) { + if (((stepDoublingTimeIC95 / stepDoublingTimeAvg) <= (UserDefinedProtocol.intervalOfConfidenceMax / 100) && (Math.abs(stepTrend / stepDoublingTimeAvg) <= (UserDefinedProtocol.growthTrendMax / 100)) && (stabilizedTime <= experimentDuration))) { theAccessory.context().put('modeStabilized', 1) theExperiment.addEvent('*** Stabilized doubling time Dt (' + theGroup.getAccessory('thermo.thermo-reg').getValue() + ' ' + String.fromCharCode(176) + 'C, ' + theAccessory.context().getString('controlledParameterText', 'no parameter') + ') is ' + round(stepDoublingTimeAvg, 2) + String.fromCharCode(177) + round(stepDoublingTimeIC95, 2) + ' h (IC95)') if (UserDefinedProtocol.particleSwarmOptimizer) { @@ -510,8 +510,8 @@ function controlPump () { debugLogger('OPTIMIZER executed with fitness ' + stepDoublingTimeAvg.toFixed(2) + ' for [ ' + positions[0].toFixed(2) + ' ] and new position is [ ' + positions[1].toFixed(2) + ' ]') theServer.sendMail('OPTIMIZER on ' + theGroup.getName() , 'NONE', ': fitness ' + stepDoublingTimeAvg.toFixed(2) + ' for [ ' + positions[0].toFixed(2) + ' ] and set new position [ ' + positions[1].toFixed(2) + ' ]') // Email notification } - theAccessory.context().put('stabilizedTime', theExperiment.getDurationSec() + UserDefinedProtocol.stabilizationTimeMin * 3600) - theAccessory.context().put('stabilizedTimeMax', theExperiment.getDurationSec() + UserDefinedProtocol.stabilizationTimeMax * 3600) + theAccessory.context().put('stabilizedTime', experimentDuration + UserDefinedProtocol.stabilizationTimeMin * 3600) + theAccessory.context().put('stabilizedTimeMax', experimentDuration + UserDefinedProtocol.stabilizationTimeMax * 3600) theAccessory.context().remove('stepCounter') theAccessory.context().remove('expDuration') theAccessory.context().remove('stepDoublingTime') @@ -522,7 +522,7 @@ function controlPump () { return theAccessory.getMax() * UserDefinedProtocol.peristalticPumpSpeed / 100 // fast } else if ((odValue <= (UserDefinedProtocol.turbidostatODMin * odMinModifier)) && pumpState) { theAccessory.context().put('modeDilution', 0) - theAccessory.context().put('lastPumpStop', theExperiment.getDurationSec()) + theAccessory.context().put('lastPumpStop', experimentDuration) debugLogger('Pump stopped.') return ProtoConfig.OFF // pump off } else if ((odValue <= (UserDefinedProtocol.turbidostatODMin * odMinModifier + ((UserDefinedProtocol.turbidostatODMax * odMaxModifier) - (UserDefinedProtocol.turbidostatODMin * odMinModifier)) * UserDefinedProtocol.peristalticPumpSlowDownRange / 100)) && pumpState) { From 43e9a0cbc572885c5782d30b5b72c5278cd494f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cerven=C3=BD?= Date: Mon, 21 Dec 2020 08:32:44 +0100 Subject: [PATCH 33/47] Multiple code improvements --- PP-GrowthOptimizer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PP-GrowthOptimizer.js b/PP-GrowthOptimizer.js index 1a8132f..fc8e024 100644 --- a/PP-GrowthOptimizer.js +++ b/PP-GrowthOptimizer.js @@ -45,8 +45,8 @@ var UserDefinedProtocol = { * @author CzechGlobe - Department of Adaptive Biotechnologies (JaCe) * @copyright Jan Červený 2020(c) * @license MIT - * @version 3.5.1 - * @modified 19.12.2020 (JaCe) + * @version 3.5.2 + * @modified 20.12.2020 (JaCe) * * @notes For proper functionality of the script "OD Regulator" protocol has to be disabled as well as chosen * controlled accessory protocols (i.e. Lights, Thermoregulation, GMS, Stirrer). From ec357f554bf6627e2c452c2318b0c09be4e93e26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20=C4=8Cerven=C3=BD?= Date: Mon, 21 Dec 2020 20:24:48 +0100 Subject: [PATCH 34/47] Fixed stabilizedTime previous accident removal --- PP-GrowthOptimizer.js | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/PP-GrowthOptimizer.js b/PP-GrowthOptimizer.js index fc8e024..9b644c1 100644 --- a/PP-GrowthOptimizer.js +++ b/PP-GrowthOptimizer.js @@ -368,6 +368,7 @@ function changeParameter(parameter, direction) { theAccessory.context().put('controlledParameterPosition', controlledParameterPosition) controlParameter(parameter, UserDefinedProtocol.controlledParameterSteps[controlledParameterPosition-1]) positions.push(UserDefinedProtocol.controlledParameterSteps[controlledParameterPosition-1]) + debugLogger('parameterChange() positions are ' + positions.toString()) return positions } // Control activity of the peristaltic pump @@ -426,20 +427,20 @@ function controlPump () { } if (theAccessory.context().getInt('stabilizedTimeMax', 0) <= experimentDuration && !pumpState) { var stepDoublingTime = theAccessory.context().get('stepDoublingTime', [ 999.9 ]) + var len = stepDoublingTime.length + var stepDoublingTimeAvg = len > 2 ? len > UserDefinedProtocol.analyzedSteps ? stepDoublingTime.slice(len - UserDefinedProtocol.analyzedSteps, len).reduce(getSumArrReduce, 0) / (UserDefinedProtocol.analyzedSteps) : stepDoublingTime.slice(1, len).reduce(getSumArrReduce, 0) / (len - 1) : stepDoublingTime[len - 1] + theAccessory.context().put('stabilizedTime', experimentDuration + UserDefinedProtocol.stabilizationTimeMin * 3600) + theAccessory.context().put('stabilizedTimeMax', experimentDuration + UserDefinedProtocol.stabilizationTimeMax * 3600) + theAccessory.context().remove('stepCounter') + theAccessory.context().remove('expDuration') + theAccessory.context().remove('stepDoublingTime') if (UserDefinedProtocol.particleSwarmOptimizer) { - var len = stepDoublingTime.length - var stepDoublingTimeAvg = len > 2 ? len > UserDefinedProtocol.analyzedSteps ? stepDoublingTime.slice(len - UserDefinedProtocol.analyzedSteps, len).reduce(getSumArrReduce, 0) / (UserDefinedProtocol.analyzedSteps) : stepDoublingTime.slice(1, len).reduce(getSumArrReduce, 0) / (len - 1) : stepDoublingTime[len - 1] PSO(stepDoublingTimeAvg) } else if (UserDefinedProtocol.controlledParameterSteps.length > 1) { var positions = changeParameter(UserDefinedProtocol.controlledParameter) debugLogger('OPTIMIZER executed on max. time with fitness ' + stepDoublingTimeAvg.toFixed(2) + ' for [ ' + positions[0].toFixed(2) + ' ] and new position is [ ' + positions[1].toFixed(2) + ' ]') theServer.sendMail('OPTIMIZER (max. time) on ' + theGroup.getName() , 'NONE', ': fitness ' + stepDoublingTimeAvg.toFixed(2) + ' for [ ' + positions[0].toFixed(2) + ' ] and set new position [ ' + positions[1].toFixed(2) + ' ]') // Email notification } - theAccessory.context().put('stabilizedTime', experimentDuration + UserDefinedProtocol.stabilizationTimeMin * 3600) - theAccessory.context().put('stabilizedTimeMax', experimentDuration + UserDefinedProtocol.stabilizationTimeMax * 3600) - theAccessory.context().remove('stepCounter') - theAccessory.context().remove('expDuration') - theAccessory.context().remove('stepDoublingTime') } // Start step growth rate evaluation if (((odValue > (UserDefinedProtocol.turbidostatODMax * odMaxModifier)) && !pumpState)) { @@ -448,6 +449,7 @@ function controlPump () { var expDuration = theAccessory.context().get('expDuration', 0.0) var stepDuration = theAccessory.context().get('stepDuration', 0.0) var stepDoublingTime = theAccessory.context().get('stepDoublingTime', [ 999.9 ]) + var stabilizedTime = theAccessory.context().getInt('stabilizedTime', 0) if (!Array.isArray(expDuration)) { stepCounter = 0 expDuration = []; stepDuration = []; stepDoublingTime = [] @@ -502,6 +504,11 @@ function controlPump () { // Growth stability test and parameters control if (((stepDoublingTimeIC95 / stepDoublingTimeAvg) <= (UserDefinedProtocol.intervalOfConfidenceMax / 100) && (Math.abs(stepTrend / stepDoublingTimeAvg) <= (UserDefinedProtocol.growthTrendMax / 100)) && (stabilizedTime <= experimentDuration))) { theAccessory.context().put('modeStabilized', 1) + theAccessory.context().put('stabilizedTime', experimentDuration + UserDefinedProtocol.stabilizationTimeMin * 3600) + theAccessory.context().put('stabilizedTimeMax', experimentDuration + UserDefinedProtocol.stabilizationTimeMax * 3600) + theAccessory.context().remove('stepCounter') + theAccessory.context().remove('expDuration') + theAccessory.context().remove('stepDoublingTime') theExperiment.addEvent('*** Stabilized doubling time Dt (' + theGroup.getAccessory('thermo.thermo-reg').getValue() + ' ' + String.fromCharCode(176) + 'C, ' + theAccessory.context().getString('controlledParameterText', 'no parameter') + ') is ' + round(stepDoublingTimeAvg, 2) + String.fromCharCode(177) + round(stepDoublingTimeIC95, 2) + ' h (IC95)') if (UserDefinedProtocol.particleSwarmOptimizer) { PSO(stepDoublingTimeAvg) @@ -510,11 +517,6 @@ function controlPump () { debugLogger('OPTIMIZER executed with fitness ' + stepDoublingTimeAvg.toFixed(2) + ' for [ ' + positions[0].toFixed(2) + ' ] and new position is [ ' + positions[1].toFixed(2) + ' ]') theServer.sendMail('OPTIMIZER on ' + theGroup.getName() , 'NONE', ': fitness ' + stepDoublingTimeAvg.toFixed(2) + ' for [ ' + positions[0].toFixed(2) + ' ] and set new position [ ' + positions[1].toFixed(2) + ' ]') // Email notification } - theAccessory.context().put('stabilizedTime', experimentDuration + UserDefinedProtocol.stabilizationTimeMin * 3600) - theAccessory.context().put('stabilizedTimeMax', experimentDuration + UserDefinedProtocol.stabilizationTimeMax * 3600) - theAccessory.context().remove('stepCounter') - theAccessory.context().remove('expDuration') - theAccessory.context().remove('stepDoublingTime') } } } From e651e9556a71fcb8fd0c80a7e9e7659a2ea6b985 Mon Sep 17 00:00:00 2001 From: JanCervenyy Date: Tue, 26 Jan 2021 11:42:59 +0100 Subject: [PATCH 35/47] Added (PSO) initial check for protocol state of controlled parameters --- PP-GrowthOptimizer.js | 119 ++++++++++++++++++++++-------------------- 1 file changed, 63 insertions(+), 56 deletions(-) diff --git a/PP-GrowthOptimizer.js b/PP-GrowthOptimizer.js index 9b644c1..5f76069 100644 --- a/PP-GrowthOptimizer.js +++ b/PP-GrowthOptimizer.js @@ -45,8 +45,8 @@ var UserDefinedProtocol = { * @author CzechGlobe - Department of Adaptive Biotechnologies (JaCe) * @copyright Jan Červený 2020(c) * @license MIT - * @version 3.5.2 - * @modified 20.12.2020 (JaCe) + * @version 3.5.3 + * @modified 26.1.2021 (JaCe) * * @notes For proper functionality of the script "OD Regulator" protocol has to be disabled as well as chosen * controlled accessory protocols (i.e. Lights, Thermoregulation, GMS, Stirrer). @@ -108,63 +108,70 @@ if (!theAccessory.context().getInt('initiated', 0)) { light1String = 'actinic-lights.light-Blue' } theAccessory.context().put('light1String', light1String) - switch (UserDefinedProtocol.controlledParameter) { - case 'lights': - if (theGroup.getAccessory('actinic-lights.light-Red').getProtoConfigValue()) { - theExperiment.addEvent('!!! Disable RED LIGHT protocol') - } - if (light1String === 'actinic-lights.light-Blue') { + if (UserDefinedProtocol.particleSwarmOptimizer) { + var parameters = UserDefinedProtocol.controlledParameters + } else { + var parameters = UserDefinedProtocol.controlledParameter + } + for (x of parameters) { + switch (UserDefinedProtocol.controlledParameter) { + case 'lights': + if (theGroup.getAccessory('actinic-lights.light-Red').getProtoConfigValue()) { + theExperiment.addEvent('!!! Disable RED LIGHT protocol') + } + if (light1String === 'actinic-lights.light-Blue') { + if (theGroup.getAccessory('actinic-lights.light-Blue').getProtoConfigValue()) { + theExperiment.addEvent('!!! Disable BLUE LIGHT protocol') + } + } else if (theGroup.getAccessory('actinic-lights.light-White').getProtoConfigValue()) { + theExperiment.addEvent('!!! Disable WHITE LIGHT protocol') + } + break + case 'light-red': + if (theGroup.getAccessory('actinic-lights.light-Red').getProtoConfigValue()) { + theExperiment.addEvent('!!! Disable RED LIGHT protocol') + } + break + case 'light-blue': if (theGroup.getAccessory('actinic-lights.light-Blue').getProtoConfigValue()) { theExperiment.addEvent('!!! Disable BLUE LIGHT protocol') } - } else if (theGroup.getAccessory('actinic-lights.light-White').getProtoConfigValue()) { - theExperiment.addEvent('!!! Disable WHITE LIGHT protocol') - } - break - case 'light-red': - if (theGroup.getAccessory('actinic-lights.light-Red').getProtoConfigValue()) { - theExperiment.addEvent('!!! Disable RED LIGHT protocol') - } - break - case 'light-blue': - if (theGroup.getAccessory('actinic-lights.light-Blue').getProtoConfigValue()) { - theExperiment.addEvent('!!! Disable RED LIGHT protocol') - } - break - case 'light-white': - if (theGroup.getAccessory('actinic-lights.light-Blue').getProtoConfigValue()) { - theExperiment.addEvent('!!! Disable RED LIGHT protocol') - } - break - case 'temperature': - if (theGroup.getAccessory('thermo.thermo-reg').getProtoConfigValue()) { - theExperiment.addEvent('!!! Disable THERMOREGULATOR protocol') - } - break - case 'GMS': - if (UserDefinedProtocol.groupGMS.getAccessory('gas-mixer.valve-0-reg').getProtoConfigValue()) { - theExperiment.addEvent('!!! Disable GMS CO2 protocol') - } - if (UserDefinedProtocol.groupGMS.getAccessory('gas-mixer.valve-1-reg').getProtoConfigValue()) { - theExperiment.addEvent('!!! Disable GMS Air/N2 protocol') - } - break - case 'GMS-CO2': - if (UserDefinedProtocol.groupGMS.getAccessory('gas-mixer.valve-0-reg').getProtoConfigValue()) { - theExperiment.addEvent('!!! Disable GMS CO2 protocol') - } - break - case 'stirrer': - if (theGroup.getAccessory('pwm.stirrer').getProtoConfigValue()) { - theExperiment.addEvent('!!! Disable STIRRER protocol') - } - break - case 'ODRange': - break - case 'none': - break - default: - theExperiment.addEvent('!!! Unknown parameter set for control - check controlledParameter setting') + break + case 'light-white': + if (theGroup.getAccessory('actinic-lights.light-Blue').getProtoConfigValue()) { + theExperiment.addEvent('!!! Disable WHITE LIGHT protocol') + } + break + case 'temperature': + if (theGroup.getAccessory('thermo.thermo-reg').getProtoConfigValue()) { + theExperiment.addEvent('!!! Disable THERMOREGULATOR protocol') + } + break + case 'GMS': + if (UserDefinedProtocol.groupGMS.getAccessory('gas-mixer.valve-0-reg').getProtoConfigValue()) { + theExperiment.addEvent('!!! Disable GMS CO2 protocol') + } + if (UserDefinedProtocol.groupGMS.getAccessory('gas-mixer.valve-1-reg').getProtoConfigValue()) { + theExperiment.addEvent('!!! Disable GMS Air/N2 protocol') + } + break + case 'GMS-CO2': + if (UserDefinedProtocol.groupGMS.getAccessory('gas-mixer.valve-0-reg').getProtoConfigValue()) { + theExperiment.addEvent('!!! Disable GMS CO2 protocol') + } + break + case 'stirrer': + if (theGroup.getAccessory('pwm.stirrer').getProtoConfigValue()) { + theExperiment.addEvent('!!! Disable STIRRER protocol') + } + break + case 'ODRange': + break + case 'none': + break + default: + theExperiment.addEvent('!!! Unknown parameter set for control - check controlledParameter setting') + } } if (UserDefinedProtocol.turbidostatODType === 680 || UserDefinedProtocol.regressionODType === 680) { if (Number(theGroup.getAccessory('od-sensors.od-680').getProtoConfigValue()) !== UserDefinedProtocol.ODReadoutInterval) { From 7d238d5d79a206f93a99b1b12f5c80dc103101e9 Mon Sep 17 00:00:00 2001 From: JanCervenyy Date: Tue, 26 Jan 2021 12:01:41 +0100 Subject: [PATCH 36/47] Added (PSO) initial check for protocol state of controlled parameters --- PP-GrowthOptimizer.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/PP-GrowthOptimizer.js b/PP-GrowthOptimizer.js index 5f76069..98b8c4a 100644 --- a/PP-GrowthOptimizer.js +++ b/PP-GrowthOptimizer.js @@ -109,12 +109,12 @@ if (!theAccessory.context().getInt('initiated', 0)) { } theAccessory.context().put('light1String', light1String) if (UserDefinedProtocol.particleSwarmOptimizer) { - var parameters = UserDefinedProtocol.controlledParameters + var parameters = UserDefinedProtocol.controlledParameters } else { - var parameters = UserDefinedProtocol.controlledParameter + var parameters = [ UserDefinedProtocol.controlledParameter ] } - for (x of parameters) { - switch (UserDefinedProtocol.controlledParameter) { + for (var i = 0; i < parameters.length; i++) { + switch (parameters[i]) { case 'lights': if (theGroup.getAccessory('actinic-lights.light-Red').getProtoConfigValue()) { theExperiment.addEvent('!!! Disable RED LIGHT protocol') From 775f6d6a402febebfef5ad2ea10f8440774c4949 Mon Sep 17 00:00:00 2001 From: JanCervenyy Date: Tue, 26 Jan 2021 12:26:47 +0100 Subject: [PATCH 37/47] Added (PSO) initial check for protocol state of controlled parameters and IC set --- PP-GrowthOptimizer.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/PP-GrowthOptimizer.js b/PP-GrowthOptimizer.js index 98b8c4a..d0b3c2b 100644 --- a/PP-GrowthOptimizer.js +++ b/PP-GrowthOptimizer.js @@ -110,8 +110,10 @@ if (!theAccessory.context().getInt('initiated', 0)) { theAccessory.context().put('light1String', light1String) if (UserDefinedProtocol.particleSwarmOptimizer) { var parameters = UserDefinedProtocol.controlledParameters + var values = UserDefinedProtocol.controlledParametersIC } else { var parameters = [ UserDefinedProtocol.controlledParameter ] + var values = UserDefinedProtocol.controlledParameterSteps } for (var i = 0; i < parameters.length; i++) { switch (parameters[i]) { @@ -171,7 +173,9 @@ if (!theAccessory.context().getInt('initiated', 0)) { break default: theExperiment.addEvent('!!! Unknown parameter set for control - check controlledParameter setting') + continue } + controlParameter(parameters[i], values[i]) } if (UserDefinedProtocol.turbidostatODType === 680 || UserDefinedProtocol.regressionODType === 680) { if (Number(theGroup.getAccessory('od-sensors.od-680').getProtoConfigValue()) !== UserDefinedProtocol.ODReadoutInterval) { @@ -197,7 +201,6 @@ if (!theAccessory.context().getInt('initiated', 0)) { debugLogger('OD range reversed.') theExperiment.addEvent('OD range set in reversed order - will be automatically corrected.') } - controlParameter(UserDefinedProtocol.controlledParameter, UserDefinedProtocol.controlledParameterSteps[0]) theAccessory.context().put('initiated', 1) debugLogger('Peristaltic Pump - Growth Optimizer initialization successful.') } catch (error) { From e0bf4a6e09e653d23196d66278db9eddb0b9f69a Mon Sep 17 00:00:00 2001 From: JanCervenyy Date: Fri, 19 Mar 2021 18:24:58 +0100 Subject: [PATCH 38/47] Initial carbonization mod --- PP-GrowthOptimizer.js | 60 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 55 insertions(+), 5 deletions(-) diff --git a/PP-GrowthOptimizer.js b/PP-GrowthOptimizer.js index d0b3c2b..5325081 100644 --- a/PP-GrowthOptimizer.js +++ b/PP-GrowthOptimizer.js @@ -31,7 +31,11 @@ var UserDefinedProtocol = { peristalticPumpSlowDownFactor: 75, // -advanced options growthRateEvalDelay: 420, - groupGMS: theGroup + groupGMS: theGroup, + mediaCarbonization: false, + carbonizationOff: 4.2, + carbonizationNormal: 8.4, + carbonizationBoost: 37.0 } /* global @@ -45,8 +49,8 @@ var UserDefinedProtocol = { * @author CzechGlobe - Department of Adaptive Biotechnologies (JaCe) * @copyright Jan Červený 2020(c) * @license MIT - * @version 3.5.3 - * @modified 26.1.2021 (JaCe) + * @version 3.6.0 + * @modified 19.3.2021 (JaCe) * * @notes For proper functionality of the script "OD Regulator" protocol has to be disabled as well as chosen * controlled accessory protocols (i.e. Lights, Thermoregulation, GMS, Stirrer). @@ -66,7 +70,7 @@ var UserDefinedProtocol = { * stirrer = [ 30, 50, 65, 80, 95 ]; // [%] !!! works only with SW version 0.7.14 and later * ODRange = [[0.4, 0.425], [0.2, 0.215], [0.1, 0.113]]; // [AU] * -optimizer stability check - * @param {number} growthStatistics [true/false] - Enable or disable calculation of growth statistics. Note that the doubling time (Dt) calculation also includes information about the fit coefficient of determination (CoD in %), known as R-squared + * @param {boolean} growthStatistics [true/false] - Enable or disable calculation of growth statistics. Note that the doubling time (Dt) calculation also includes information about the fit coefficient of determination (CoD in %), known as R-squared * @param {number} regressionODType [680/720/735] - OD sensor used for doubling time determination * @param {number} regressionCoDMin [%] - Minimum accpeted coefficient of determination for staility check evaluation (values below are ignored) * @param {number} stabilizationTimeMin [h] - Minimum duration of each characterization step @@ -84,6 +88,10 @@ var UserDefinedProtocol = { * @param {number} growthRateEvalDelay [s] - Time after dilution where data for doubling time determination are ignored. By default growthRateEvalFrac, i.e. only limited fraction of the data points is used for calculations. * This is to prevent influence of post dilution effect on doubling time evaluation. If 0 or false, growthRateEvalDelay is used instead. Note that to completely disable data limitation you need to set both growthRateEvalFrac and growthRateEvalDelay to 0. * @param {string} groupGMS - Identifies the group that contains Gas Mixing System. System value - do not change unless sure what you are doing! + * @param {boolean} mediaCarbonization [true/false] - Enable or disable media carbonization during turbidostat pump action. + * @param {double} carbonizationOff [ml/min] - GMS CO2 channel flow when no carbonization of media is activated. + * @param {double} carbonizationNormal [ml/min] - GMS CO2 channel flow when weak carbonization of media is activated. + * @param {double} carbonizationBoost [ml/min] - GMS CO2 channel flow when intensive carbonization of media is activated. * * @return Flow of external/additional pump * @@ -208,6 +216,27 @@ if (!theAccessory.context().getInt('initiated', 0)) { } } +// Trigger media carbonization +if (UserDefinedProtocol.groupGMS === theGroup) { + try { + switch(theAccessory.context().getInt('carbonization', 0)) { + case -1: + theGroup.getAccessory('gas-mixer.valve-0-reg').setRunningProtoConfig(new ProtoConfig(UserDefinedProtocol.carbonizationOff)) + theAccessory.context().put('carbonization', 0) + break + case 1: + theGroup.getAccessory('gas-mixer.valve-0-reg').setRunningProtoConfig(new ProtoConfig(UserDefinedProtocol.carbonizationNormal)) + break + case 2: + theGroup.getAccessory('gas-mixer.valve-0-reg').setRunningProtoConfig(new ProtoConfig(UserDefinedProtocol.carbonizationBoost)) + break + default: + } + } catch (error) { + debugLogger('Carbonization ERROR. ' + error.name + ' : ' + error.message) + } +} + // Set the pump try { var pumpState = !isNaN(theAccessory.getValue()) @@ -531,14 +560,35 @@ function controlPump () { } } debugLogger('Pump max speed.') + if (UserDefinedProtocol.mediaCarbonization && ((theAccessory.context().getDouble('pHpostDilution',7.0) - theAccessory.context().getDouble('pHpreDilution',7.0)) > 0)) { + debugLogger('Carbonization initiated') + theAccessory.context().put('carbonization', 1) + theServer.getGroupByName(UserDefinedProtocol.groupGMS).getAccessory('pumps.pump-5').context().put('carbonization', 1) + } + theAccessory.context().put('pHpreDilution', theGroup.getAccessory('probes.ph').getValue()) return theAccessory.getMax() * UserDefinedProtocol.peristalticPumpSpeed / 100 // fast } else if ((odValue <= (UserDefinedProtocol.turbidostatODMin * odMinModifier)) && pumpState) { theAccessory.context().put('modeDilution', 0) theAccessory.context().put('lastPumpStop', experimentDuration) debugLogger('Pump stopped.') + if (theAccessory.context().get('carbonization', 0)) { + debugLogger('Carbonization terminated') + theAccessory.context().put('carbonization', 0) + theServer.getGroupByName(UserDefinedProtocol.groupGMS).getAccessory('pumps.pump-5').context().put('carbonization', -1) + } + theAccessory.context().put('pHpostDilution', theGroup.getAccessory('probes.ph').getValue()) return ProtoConfig.OFF // pump off } else if ((odValue <= (UserDefinedProtocol.turbidostatODMin * odMinModifier + ((UserDefinedProtocol.turbidostatODMax * odMaxModifier) - (UserDefinedProtocol.turbidostatODMin * odMinModifier)) * UserDefinedProtocol.peristalticPumpSlowDownRange / 100)) && pumpState) { - debugLogger('Pump low speed.', 0) + debugLogger('Pump low speed', 0) + if (UserDefinedProtocol.mediaCarbonization && ((theGroup.getAccessory('probes.ph').getValue() - theAccessory.context().getDouble('pHpreDilution',7.0)) > 0) && !theAccessory.context().get('carbonization', 0)) { + debugLogger('Carbonization boost') + theAccessory.context().put('carbonization', 1) + theServer.getGroupByName(UserDefinedProtocol.groupGMS).getAccessory('pumps.pump-5').context().put('carbonization', 2) + } else { + debugLogger('Carbonization terminated') + theAccessory.context().put('carbonization', 0) + theServer.getGroupByName(UserDefinedProtocol.groupGMS).getAccessory('pumps.pump-5').context().put('carbonization', -1) + } return theAccessory.getMax() * UserDefinedProtocol.peristalticPumpSpeed / 100 * UserDefinedProtocol.peristalticPumpSlowDownFactor / 100 // slow down the pump } else { return null // pump not influenced From 0e73e2d79f81d99eec6586dcbda3ae3f54974784 Mon Sep 17 00:00:00 2001 From: JanCervenyy Date: Sun, 21 Mar 2021 08:25:57 +0100 Subject: [PATCH 39/47] Carbonization boost better handling --- PP-GrowthOptimizer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PP-GrowthOptimizer.js b/PP-GrowthOptimizer.js index 5325081..90e4658 100644 --- a/PP-GrowthOptimizer.js +++ b/PP-GrowthOptimizer.js @@ -580,9 +580,9 @@ function controlPump () { return ProtoConfig.OFF // pump off } else if ((odValue <= (UserDefinedProtocol.turbidostatODMin * odMinModifier + ((UserDefinedProtocol.turbidostatODMax * odMaxModifier) - (UserDefinedProtocol.turbidostatODMin * odMinModifier)) * UserDefinedProtocol.peristalticPumpSlowDownRange / 100)) && pumpState) { debugLogger('Pump low speed', 0) - if (UserDefinedProtocol.mediaCarbonization && ((theGroup.getAccessory('probes.ph').getValue() - theAccessory.context().getDouble('pHpreDilution',7.0)) > 0) && !theAccessory.context().get('carbonization', 0)) { + if (UserDefinedProtocol.mediaCarbonization && ((theGroup.getAccessory('probes.ph').getValue() - theAccessory.context().getDouble('pHpreDilution',7.0)) > 0) && (theAccessory.context().get('carbonization', 0) < 2)) { debugLogger('Carbonization boost') - theAccessory.context().put('carbonization', 1) + theAccessory.context().put('carbonization', 2) theServer.getGroupByName(UserDefinedProtocol.groupGMS).getAccessory('pumps.pump-5').context().put('carbonization', 2) } else { debugLogger('Carbonization terminated') From b7765e7ae8e934b1092f84bcb9b2162de2288162 Mon Sep 17 00:00:00 2001 From: JanCervenyy Date: Sun, 21 Mar 2021 09:41:03 +0100 Subject: [PATCH 40/47] Revert "Carbonization boost better handling" This reverts commit 0e73e2d79f81d99eec6586dcbda3ae3f54974784. --- PP-GrowthOptimizer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PP-GrowthOptimizer.js b/PP-GrowthOptimizer.js index 90e4658..5325081 100644 --- a/PP-GrowthOptimizer.js +++ b/PP-GrowthOptimizer.js @@ -580,9 +580,9 @@ function controlPump () { return ProtoConfig.OFF // pump off } else if ((odValue <= (UserDefinedProtocol.turbidostatODMin * odMinModifier + ((UserDefinedProtocol.turbidostatODMax * odMaxModifier) - (UserDefinedProtocol.turbidostatODMin * odMinModifier)) * UserDefinedProtocol.peristalticPumpSlowDownRange / 100)) && pumpState) { debugLogger('Pump low speed', 0) - if (UserDefinedProtocol.mediaCarbonization && ((theGroup.getAccessory('probes.ph').getValue() - theAccessory.context().getDouble('pHpreDilution',7.0)) > 0) && (theAccessory.context().get('carbonization', 0) < 2)) { + if (UserDefinedProtocol.mediaCarbonization && ((theGroup.getAccessory('probes.ph').getValue() - theAccessory.context().getDouble('pHpreDilution',7.0)) > 0) && !theAccessory.context().get('carbonization', 0)) { debugLogger('Carbonization boost') - theAccessory.context().put('carbonization', 2) + theAccessory.context().put('carbonization', 1) theServer.getGroupByName(UserDefinedProtocol.groupGMS).getAccessory('pumps.pump-5').context().put('carbonization', 2) } else { debugLogger('Carbonization terminated') From b365768d55f4f20271256eadf8ae775246fcbedf Mon Sep 17 00:00:00 2001 From: JanCervenyy Date: Sun, 21 Mar 2021 09:43:56 +0100 Subject: [PATCH 41/47] Better boosted carbonization handling --- PP-GrowthOptimizer.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/PP-GrowthOptimizer.js b/PP-GrowthOptimizer.js index 5325081..65b07d1 100644 --- a/PP-GrowthOptimizer.js +++ b/PP-GrowthOptimizer.js @@ -580,11 +580,11 @@ function controlPump () { return ProtoConfig.OFF // pump off } else if ((odValue <= (UserDefinedProtocol.turbidostatODMin * odMinModifier + ((UserDefinedProtocol.turbidostatODMax * odMaxModifier) - (UserDefinedProtocol.turbidostatODMin * odMinModifier)) * UserDefinedProtocol.peristalticPumpSlowDownRange / 100)) && pumpState) { debugLogger('Pump low speed', 0) - if (UserDefinedProtocol.mediaCarbonization && ((theGroup.getAccessory('probes.ph').getValue() - theAccessory.context().getDouble('pHpreDilution',7.0)) > 0) && !theAccessory.context().get('carbonization', 0)) { + if (UserDefinedProtocol.mediaCarbonization && ((theGroup.getAccessory('probes.ph').getValue() - theAccessory.context().getDouble('pHpreDilution',7.0)) > 0) && (theAccessory.context().get('carbonization', 0) < 2)) { debugLogger('Carbonization boost') - theAccessory.context().put('carbonization', 1) + theAccessory.context().put('carbonization', 2) theServer.getGroupByName(UserDefinedProtocol.groupGMS).getAccessory('pumps.pump-5').context().put('carbonization', 2) - } else { + } else if (UserDefinedProtocol.mediaCarbonization && (theAccessory.context().get('carbonization', 0) == 1)) { debugLogger('Carbonization terminated') theAccessory.context().put('carbonization', 0) theServer.getGroupByName(UserDefinedProtocol.groupGMS).getAccessory('pumps.pump-5').context().put('carbonization', -1) From a03ceec74e036fc6ac51410cbc33a40cd7bf1e8c Mon Sep 17 00:00:00 2001 From: JanCervenyy Date: Mon, 22 Mar 2021 21:38:45 +0100 Subject: [PATCH 42/47] Improved groupGMS addresing --- PP-GrowthOptimizer.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/PP-GrowthOptimizer.js b/PP-GrowthOptimizer.js index 65b07d1..a8e515d 100644 --- a/PP-GrowthOptimizer.js +++ b/PP-GrowthOptimizer.js @@ -87,7 +87,7 @@ var UserDefinedProtocol = { * -advanced options * @param {number} growthRateEvalDelay [s] - Time after dilution where data for doubling time determination are ignored. By default growthRateEvalFrac, i.e. only limited fraction of the data points is used for calculations. * This is to prevent influence of post dilution effect on doubling time evaluation. If 0 or false, growthRateEvalDelay is used instead. Note that to completely disable data limitation you need to set both growthRateEvalFrac and growthRateEvalDelay to 0. - * @param {string} groupGMS - Identifies the group that contains Gas Mixing System. System value - do not change unless sure what you are doing! + * @param {object} groupGMS - Identifies the group that contains Gas Mixing System. System value - do not change unless sure what you are doing! Example: theServer.getGroupByName('GROUP-NAME') * @param {boolean} mediaCarbonization [true/false] - Enable or disable media carbonization during turbidostat pump action. * @param {double} carbonizationOff [ml/min] - GMS CO2 channel flow when no carbonization of media is activated. * @param {double} carbonizationNormal [ml/min] - GMS CO2 channel flow when weak carbonization of media is activated. @@ -563,7 +563,7 @@ function controlPump () { if (UserDefinedProtocol.mediaCarbonization && ((theAccessory.context().getDouble('pHpostDilution',7.0) - theAccessory.context().getDouble('pHpreDilution',7.0)) > 0)) { debugLogger('Carbonization initiated') theAccessory.context().put('carbonization', 1) - theServer.getGroupByName(UserDefinedProtocol.groupGMS).getAccessory('pumps.pump-5').context().put('carbonization', 1) + UserDefinedProtocol.groupGMS.getAccessory('pumps.pump-5').context().put('carbonization', 1) } theAccessory.context().put('pHpreDilution', theGroup.getAccessory('probes.ph').getValue()) return theAccessory.getMax() * UserDefinedProtocol.peristalticPumpSpeed / 100 // fast @@ -574,7 +574,7 @@ function controlPump () { if (theAccessory.context().get('carbonization', 0)) { debugLogger('Carbonization terminated') theAccessory.context().put('carbonization', 0) - theServer.getGroupByName(UserDefinedProtocol.groupGMS).getAccessory('pumps.pump-5').context().put('carbonization', -1) + UserDefinedProtocol.groupGMS.getAccessory('pumps.pump-5').context().put('carbonization', -1) } theAccessory.context().put('pHpostDilution', theGroup.getAccessory('probes.ph').getValue()) return ProtoConfig.OFF // pump off @@ -583,11 +583,11 @@ function controlPump () { if (UserDefinedProtocol.mediaCarbonization && ((theGroup.getAccessory('probes.ph').getValue() - theAccessory.context().getDouble('pHpreDilution',7.0)) > 0) && (theAccessory.context().get('carbonization', 0) < 2)) { debugLogger('Carbonization boost') theAccessory.context().put('carbonization', 2) - theServer.getGroupByName(UserDefinedProtocol.groupGMS).getAccessory('pumps.pump-5').context().put('carbonization', 2) + UserDefinedProtocol.groupGMS.getAccessory('pumps.pump-5').context().put('carbonization', 2) } else if (UserDefinedProtocol.mediaCarbonization && (theAccessory.context().get('carbonization', 0) == 1)) { debugLogger('Carbonization terminated') theAccessory.context().put('carbonization', 0) - theServer.getGroupByName(UserDefinedProtocol.groupGMS).getAccessory('pumps.pump-5').context().put('carbonization', -1) + UserDefinedProtocol.groupGMS.getAccessory('pumps.pump-5').context().put('carbonization', -1) } return theAccessory.getMax() * UserDefinedProtocol.peristalticPumpSpeed / 100 * UserDefinedProtocol.peristalticPumpSlowDownFactor / 100 // slow down the pump } else { From 1501261003034029e69f0fdf8c15be66e8e18046 Mon Sep 17 00:00:00 2001 From: JanCervenyy Date: Thu, 22 Apr 2021 16:51:09 +0200 Subject: [PATCH 43/47] White light addressing corrected --- PP-GrowthOptimizer.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/PP-GrowthOptimizer.js b/PP-GrowthOptimizer.js index a8e515d..c08d367 100644 --- a/PP-GrowthOptimizer.js +++ b/PP-GrowthOptimizer.js @@ -1,7 +1,7 @@ var UserDefinedProtocol = { // -turbidostat settings - turbidostatODMin: 0.18, - turbidostatODMax: 0.195, + turbidostatODMin: 0.3325, + turbidostatODMax: 0.3367, turbidostatODType: 720, ODReadoutInterval: 60, // -optimizer parameters @@ -49,8 +49,8 @@ var UserDefinedProtocol = { * @author CzechGlobe - Department of Adaptive Biotechnologies (JaCe) * @copyright Jan Červený 2020(c) * @license MIT - * @version 3.6.0 - * @modified 19.3.2021 (JaCe) + * @version 3.6.1 + * @modified 21.4.2021 (JaCe) * * @notes For proper functionality of the script "OD Regulator" protocol has to be disabled as well as chosen * controlled accessory protocols (i.e. Lights, Thermoregulation, GMS, Stirrer). @@ -148,7 +148,7 @@ if (!theAccessory.context().getInt('initiated', 0)) { } break case 'light-white': - if (theGroup.getAccessory('actinic-lights.light-Blue').getProtoConfigValue()) { + if (theGroup.getAccessory('actinic-lights.light-White').getProtoConfigValue()) { theExperiment.addEvent('!!! Disable WHITE LIGHT protocol') } break From f442aa9fed3555cf206925abc38e20c8f3830650 Mon Sep 17 00:00:00 2001 From: JanCervenyy Date: Mon, 26 Apr 2021 12:49:13 +0200 Subject: [PATCH 44/47] IC95 to CI95 global renaming --- PP-GrowthOptimizer.js | 83 +++++++++++++++++++++++-------------------- 1 file changed, 44 insertions(+), 39 deletions(-) diff --git a/PP-GrowthOptimizer.js b/PP-GrowthOptimizer.js index c08d367..f64bacf 100644 --- a/PP-GrowthOptimizer.js +++ b/PP-GrowthOptimizer.js @@ -17,13 +17,13 @@ var UserDefinedProtocol = { // -optimizer stability check growthStatistics: true, regressionODType: 680, - regressionCoDMin: 60, - stabilizationTimeMin: 3, - stabilizationTimeMax: 7, + regressionCoDMin: 75, + stabilizationTimeMin: 8, + stabilizationTimeMax: 24, growthRateEvalFrac: 3 / 4, analyzedSteps: 6, - growthTrendMax: 15, - intervalOfConfidenceMax: 35, + growthTrendMax: 1.5, + CI95AmplitudeMax: 2.5, // -peristaltic pump settings peristalticPumpID: 5, peristalticPumpSpeed: 100, @@ -49,8 +49,8 @@ var UserDefinedProtocol = { * @author CzechGlobe - Department of Adaptive Biotechnologies (JaCe) * @copyright Jan Červený 2020(c) * @license MIT - * @version 3.6.1 - * @modified 21.4.2021 (JaCe) + * @version 3.6.2 + * @modified 26.4.2021 (JaCe) * * @notes For proper functionality of the script "OD Regulator" protocol has to be disabled as well as chosen * controlled accessory protocols (i.e. Lights, Thermoregulation, GMS, Stirrer). @@ -78,7 +78,7 @@ var UserDefinedProtocol = { * @param {number} growthRateEvalFrac [0-1] - Defines whether to use particular fraction of the data points for doubling time determination. * @param {number} analyzedSteps [-] - Number of steps to be analyzed for stability check * @param {number} growthTrendMax [%] - Maximum growth speed trend in time - * @param {number} intervalOfConfidenceMax [%] - Maximum allowed percents of 95% Confidence Interval + * @param {number} intervalOfConfidenceAmplitudeMax [%] - Maximum allowed amblitude of 95% Confidence Interval in percents from mean * -peristaltic pump settings * @param {number} peristalticPumpID [3-7] - Defines peristaltic pump ID set to the pump that is used for fresh media supply (quasi-continuous mode) * @param {number} peristalticPumpSpeed [%] - Nominal pump speed used for dilution of the suspension @@ -104,7 +104,7 @@ importPackage(Packages.psi.bioreactor.core.protocol) importPackage(Packages.psi.bioreactor.core.regression) // Inicialization of the control script -if (!theAccessory.context().getInt('initiated', 0)) { +if (!theAccessory.context().get('initiated', false)) { try { theAccessory.context().clear() theAccessory.context().put('stabilizedTime', Number(theExperiment.getDurationSec()) + UserDefinedProtocol.stabilizationTimeMin * 3600) @@ -209,7 +209,7 @@ if (!theAccessory.context().getInt('initiated', 0)) { debugLogger('OD range reversed.') theExperiment.addEvent('OD range set in reversed order - will be automatically corrected.') } - theAccessory.context().put('initiated', 1) + theAccessory.context().put('initiated', true) debugLogger('Peristaltic Pump - Growth Optimizer initialization successful.') } catch (error) { debugLogger('Initialization ERROR. ' + error.name + ' : ' + error.message) @@ -407,7 +407,6 @@ function changeParameter(parameter, direction) { theAccessory.context().put('controlledParameterPosition', controlledParameterPosition) controlParameter(parameter, UserDefinedProtocol.controlledParameterSteps[controlledParameterPosition-1]) positions.push(UserDefinedProtocol.controlledParameterSteps[controlledParameterPosition-1]) - debugLogger('parameterChange() positions are ' + positions.toString()) return positions } // Control activity of the peristaltic pump @@ -470,6 +469,7 @@ function controlPump () { var stepDoublingTimeAvg = len > 2 ? len > UserDefinedProtocol.analyzedSteps ? stepDoublingTime.slice(len - UserDefinedProtocol.analyzedSteps, len).reduce(getSumArrReduce, 0) / (UserDefinedProtocol.analyzedSteps) : stepDoublingTime.slice(1, len).reduce(getSumArrReduce, 0) / (len - 1) : stepDoublingTime[len - 1] theAccessory.context().put('stabilizedTime', experimentDuration + UserDefinedProtocol.stabilizationTimeMin * 3600) theAccessory.context().put('stabilizedTimeMax', experimentDuration + UserDefinedProtocol.stabilizationTimeMax * 3600) + theAccessory.context().remove('stepAccumulated') theAccessory.context().remove('stepCounter') theAccessory.context().remove('expDuration') theAccessory.context().remove('stepDoublingTime') @@ -511,50 +511,55 @@ function controlPump () { if (stepCounter >= UserDefinedProtocol.analyzedSteps) { var stepDoublingTimeAvg = 0 var stepDoublingTimeSD = 0 - var stepDoublingTimeIC95 = 0 + var stepDoublingTimeCI95 = 0 var stepTrend = 0 + var stepAccumulated = theAccessory.context().getInt('stepAccumulated', 0) var sumXY = 0 var sumX = 0 var sumY = 0 var sumX2 = 0 // var sumY2 = 0 - // TODO if trend then accumulate steps as [analyzedSteps inf] // Average of steps doubling time - for (var i = (stepCounter - 1); i >= (stepCounter - UserDefinedProtocol.analyzedSteps); i--) { + for (var i = (stepCounter - 1); i >= (stepCounter - UserDefinedProtocol.analyzedSteps - stepAccumulated); i--) { stepDoublingTimeAvg += Number(stepDoublingTime[i]) } - stepDoublingTimeAvg /= UserDefinedProtocol.analyzedSteps - // IC95 of steps doubling time - for (i = (stepCounter - 1); i >= (stepCounter - UserDefinedProtocol.analyzedSteps); i--) { + stepDoublingTimeAvg /= (UserDefinedProtocol.analyzedSteps + stepAccumulated) + // CI95 of steps doubling time + for (i = (stepCounter - 1); i >= (stepCounter - UserDefinedProtocol.analyzedSteps - stepAccumulated); i--) { stepDoublingTimeSD += Math.pow(stepDoublingTime[i] - stepDoublingTimeAvg, 2) } - stepDoublingTimeSD = Math.sqrt(stepDoublingTimeSD / (UserDefinedProtocol.analyzedSteps - 1)) - stepDoublingTimeIC95 = stepDoublingTimeSD / Math.sqrt(UserDefinedProtocol.analyzedSteps) * 1.96 + stepDoublingTimeSD = Math.sqrt(stepDoublingTimeSD / (UserDefinedProtocol.analyzedSteps + stepAccumulated - 1)) + stepDoublingTimeCI95 = stepDoublingTimeSD / Math.sqrt(UserDefinedProtocol.analyzedSteps + stepAccumulated) * 1.96 // Trend of steps doubling time - for (i = (stepCounter - 1); i >= (stepCounter - UserDefinedProtocol.analyzedSteps); i--) { + for (i = (stepCounter - 1); i >= (stepCounter - UserDefinedProtocol.analyzedSteps - stepAccumulated); i--) { sumX += Number(expDuration[i]) sumX2 += Math.pow(expDuration[i], 2) sumY += Number(stepDoublingTime[i]) // sumY2 += Math.pow(stepDoublingTime[i], 2) sumXY += Number(expDuration[i]) * Number(stepDoublingTime[i]) } - stepTrend = (UserDefinedProtocol.analyzedSteps * sumXY - sumX * sumY) / (UserDefinedProtocol.analyzedSteps * sumX2 - Math.pow(sumX, 2)) * 3600 - theExperiment.addEvent('Steps doubling time Avg: ' + round(stepDoublingTimeAvg, 2) + ' h, IC95 ' + round(stepDoublingTimeIC95, 2) + ' h (' + round(stepDoublingTimeIC95 / stepDoublingTimeAvg * 100, 1) + '%) with ' + round(stepTrend, 2) + ' h/h trend (' + round(stepTrend / stepDoublingTimeAvg * 100, 1) + '%)') + stepTrend = ((UserDefinedProtocol.analyzedSteps + stepAccumulated) * sumXY - sumX * sumY) / ((UserDefinedProtocol.analyzedSteps + stepAccumulated) * sumX2 - Math.pow(sumX, 2)) * 3600 + theExperiment.addEvent('Step doubling time Avg: ' + round(stepDoublingTimeAvg, 2) + ' h, CI95 ' + String.fromCharCode(177) + round(stepDoublingTimeCI95, 2) + ' h (' + round(stepDoublingTimeCI95 / stepDoublingTimeAvg * 100, 1) + '%) with ' + round(stepTrend, 2) + ' h/h trend (' + round(stepTrend / stepDoublingTimeAvg * 100, 1) + '%)') // Growth stability test and parameters control - if (((stepDoublingTimeIC95 / stepDoublingTimeAvg) <= (UserDefinedProtocol.intervalOfConfidenceMax / 100) && (Math.abs(stepTrend / stepDoublingTimeAvg) <= (UserDefinedProtocol.growthTrendMax / 100)) && (stabilizedTime <= experimentDuration))) { - theAccessory.context().put('modeStabilized', 1) - theAccessory.context().put('stabilizedTime', experimentDuration + UserDefinedProtocol.stabilizationTimeMin * 3600) - theAccessory.context().put('stabilizedTimeMax', experimentDuration + UserDefinedProtocol.stabilizationTimeMax * 3600) - theAccessory.context().remove('stepCounter') - theAccessory.context().remove('expDuration') - theAccessory.context().remove('stepDoublingTime') - theExperiment.addEvent('*** Stabilized doubling time Dt (' + theGroup.getAccessory('thermo.thermo-reg').getValue() + ' ' + String.fromCharCode(176) + 'C, ' + theAccessory.context().getString('controlledParameterText', 'no parameter') + ') is ' + round(stepDoublingTimeAvg, 2) + String.fromCharCode(177) + round(stepDoublingTimeIC95, 2) + ' h (IC95)') - if (UserDefinedProtocol.particleSwarmOptimizer) { - PSO(stepDoublingTimeAvg) - } else if (UserDefinedProtocol.controlledParameterSteps.length > 1) { - var positions = changeParameter(UserDefinedProtocol.controlledParameter) - debugLogger('OPTIMIZER executed with fitness ' + stepDoublingTimeAvg.toFixed(2) + ' for [ ' + positions[0].toFixed(2) + ' ] and new position is [ ' + positions[1].toFixed(2) + ' ]') - theServer.sendMail('OPTIMIZER on ' + theGroup.getName() , 'NONE', ': fitness ' + stepDoublingTimeAvg.toFixed(2) + ' for [ ' + positions[0].toFixed(2) + ' ] and set new position [ ' + positions[1].toFixed(2) + ' ]') // Email notification + if ((Math.abs(stepTrend / stepDoublingTimeAvg) <= (UserDefinedProtocol.growthTrendMax / 100)) || stepAccumulated) { + // TODO this solution removes stepTrend criteria check if at least once reached (expecting convergence!!!). May need to be upgraded later. + theAccessory.context().put('stepAccumulated', stepAccumulated + 1) + if ((stabilizedTime <= experimentDuration) && ((stepDoublingTimeCI95 / stepDoublingTimeAvg) <= (UserDefinedProtocol.CI95AmplitudeMax / 100))) { + theAccessory.context().put('modeStabilized', 1) + theAccessory.context().put('stabilizedTime', experimentDuration + UserDefinedProtocol.stabilizationTimeMin * 3600) + theAccessory.context().put('stabilizedTimeMax', experimentDuration + UserDefinedProtocol.stabilizationTimeMax * 3600) + theAccessory.context().remove('stepAccumulated') + theAccessory.context().remove('stepCounter') + theAccessory.context().remove('expDuration') + theAccessory.context().remove('stepDoublingTime') + theExperiment.addEvent('*** Stabilized doubling time Dt (' + theGroup.getAccessory('thermo.thermo-reg').getValue() + ' ' + String.fromCharCode(176) + 'C, ' + theAccessory.context().getString('controlledParameterText', 'no parameter') + ') is ' + round(stepDoublingTimeAvg, 2) + String.fromCharCode(177) + round(stepDoublingTimeCI95, 2) + ' h (CI95)') + if (UserDefinedProtocol.particleSwarmOptimizer) { + PSO(stepDoublingTimeAvg) + } else if (UserDefinedProtocol.controlledParameterSteps.length > 1) { + var positions = changeParameter(UserDefinedProtocol.controlledParameter) + debugLogger('OPTIMIZER executed with fitness ' + stepDoublingTimeAvg.toFixed(2) + ' for [ ' + positions[0].toFixed(2) + ' ] and new position is [ ' + positions[1].toFixed(2) + ' ]') + theServer.sendMail('OPTIMIZER on ' + theGroup.getName() , 'NONE', ': fitness ' + stepDoublingTimeAvg.toFixed(2) + ' for [ ' + positions[0].toFixed(2) + ' ] and set new position [ ' + positions[1].toFixed(2) + ' ]') // Email notification + } } } } @@ -571,7 +576,7 @@ function controlPump () { theAccessory.context().put('modeDilution', 0) theAccessory.context().put('lastPumpStop', experimentDuration) debugLogger('Pump stopped.') - if (theAccessory.context().get('carbonization', 0)) { + if (theAccessory.context().getInt('carbonization', 0)) { debugLogger('Carbonization terminated') theAccessory.context().put('carbonization', 0) UserDefinedProtocol.groupGMS.getAccessory('pumps.pump-5').context().put('carbonization', -1) @@ -580,11 +585,11 @@ function controlPump () { return ProtoConfig.OFF // pump off } else if ((odValue <= (UserDefinedProtocol.turbidostatODMin * odMinModifier + ((UserDefinedProtocol.turbidostatODMax * odMaxModifier) - (UserDefinedProtocol.turbidostatODMin * odMinModifier)) * UserDefinedProtocol.peristalticPumpSlowDownRange / 100)) && pumpState) { debugLogger('Pump low speed', 0) - if (UserDefinedProtocol.mediaCarbonization && ((theGroup.getAccessory('probes.ph').getValue() - theAccessory.context().getDouble('pHpreDilution',7.0)) > 0) && (theAccessory.context().get('carbonization', 0) < 2)) { + if (UserDefinedProtocol.mediaCarbonization && ((theGroup.getAccessory('probes.ph').getValue() - theAccessory.context().getDouble('pHpreDilution',7.0)) > 0) && (theAccessory.context().getInt('carbonization', 0) < 2)) { debugLogger('Carbonization boost') theAccessory.context().put('carbonization', 2) UserDefinedProtocol.groupGMS.getAccessory('pumps.pump-5').context().put('carbonization', 2) - } else if (UserDefinedProtocol.mediaCarbonization && (theAccessory.context().get('carbonization', 0) == 1)) { + } else if (UserDefinedProtocol.mediaCarbonization && (theAccessory.context().getInt('carbonization', 0) == 1)) { debugLogger('Carbonization terminated') theAccessory.context().put('carbonization', 0) UserDefinedProtocol.groupGMS.getAccessory('pumps.pump-5').context().put('carbonization', -1) From 07dc70c7b5d3036c92e055f71e3bb03e236f006e Mon Sep 17 00:00:00 2001 From: JanCervenyy Date: Sat, 1 May 2021 18:14:04 +0200 Subject: [PATCH 45/47] Logging text update --- PP-GrowthOptimizer.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/PP-GrowthOptimizer.js b/PP-GrowthOptimizer.js index f64bacf..f6f145d 100644 --- a/PP-GrowthOptimizer.js +++ b/PP-GrowthOptimizer.js @@ -1,7 +1,7 @@ var UserDefinedProtocol = { // -turbidostat settings turbidostatODMin: 0.3325, - turbidostatODMax: 0.3367, + turbidostatODMax: 0.3675, turbidostatODType: 720, ODReadoutInterval: 60, // -optimizer parameters @@ -227,7 +227,7 @@ if (UserDefinedProtocol.groupGMS === theGroup) { case 1: theGroup.getAccessory('gas-mixer.valve-0-reg').setRunningProtoConfig(new ProtoConfig(UserDefinedProtocol.carbonizationNormal)) break - case 2: + case 2: theGroup.getAccessory('gas-mixer.valve-0-reg').setRunningProtoConfig(new ProtoConfig(UserDefinedProtocol.carbonizationBoost)) break default: @@ -539,7 +539,7 @@ function controlPump () { sumXY += Number(expDuration[i]) * Number(stepDoublingTime[i]) } stepTrend = ((UserDefinedProtocol.analyzedSteps + stepAccumulated) * sumXY - sumX * sumY) / ((UserDefinedProtocol.analyzedSteps + stepAccumulated) * sumX2 - Math.pow(sumX, 2)) * 3600 - theExperiment.addEvent('Step doubling time Avg: ' + round(stepDoublingTimeAvg, 2) + ' h, CI95 ' + String.fromCharCode(177) + round(stepDoublingTimeCI95, 2) + ' h (' + round(stepDoublingTimeCI95 / stepDoublingTimeAvg * 100, 1) + '%) with ' + round(stepTrend, 2) + ' h/h trend (' + round(stepTrend / stepDoublingTimeAvg * 100, 1) + '%)') + theExperiment.addEvent('Average step doubling time is ' + round(stepDoublingTimeAvg, 2) + String.fromCharCode(177) + round(stepDoublingTimeCI95, 2) + ' h (CI95, ' + round(stepDoublingTimeCI95 / stepDoublingTimeAvg * 100, 1) + '%) with ' + round(stepTrend, 2) + ' h/h trend (' + round(stepTrend / stepDoublingTimeAvg * 100, 1) + '%)') // Growth stability test and parameters control if ((Math.abs(stepTrend / stepDoublingTimeAvg) <= (UserDefinedProtocol.growthTrendMax / 100)) || stepAccumulated) { // TODO this solution removes stepTrend criteria check if at least once reached (expecting convergence!!!). May need to be upgraded later. @@ -576,7 +576,7 @@ function controlPump () { theAccessory.context().put('modeDilution', 0) theAccessory.context().put('lastPumpStop', experimentDuration) debugLogger('Pump stopped.') - if (theAccessory.context().getInt('carbonization', 0)) { + if (UserDefinedProtocol.mediaCarbonization && theAccessory.context().getInt('carbonization', 0)) { debugLogger('Carbonization terminated') theAccessory.context().put('carbonization', 0) UserDefinedProtocol.groupGMS.getAccessory('pumps.pump-5').context().put('carbonization', -1) From 5076415cbf0371335236fc1ce8d613ddae138562 Mon Sep 17 00:00:00 2001 From: JanCervenyy Date: Tue, 11 May 2021 09:06:33 +0200 Subject: [PATCH 46/47] Fixed stepCounter for failed CoD check --- PP-GrowthOptimizer.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/PP-GrowthOptimizer.js b/PP-GrowthOptimizer.js index f6f145d..009c35d 100644 --- a/PP-GrowthOptimizer.js +++ b/PP-GrowthOptimizer.js @@ -503,12 +503,11 @@ function controlPump () { var DHCapacity = (Math.floor(stepDuration[stepCounter] / UserDefinedProtocol.ODReadoutInterval) - 3) > 0 ? (Math.floor(stepDuration[stepCounter] / UserDefinedProtocol.ODReadoutInterval) - 3) : 60 var regCoefExp = odSensorRegression.getDataHistory().regression(ETrendFunction.EXP, Math.ceil(DHCapacity - (UserDefinedProtocol.growthRateEvalFrac ? DHCapacity * (UserDefinedProtocol.growthRateEvalFrac / 100) : UserDefinedProtocol.growthRateEvalDelay / UserDefinedProtocol.ODReadoutInterval))) debugLogger('Growth parameters: A=' + regCoefExp[0] +', B=' + regCoefExp[1] + ', R2=' + regCoefExp[2]) + theExperiment.addEvent('Doubling time of the step was ' + round((1 / (Number(regCoefExp[1]) * 3600 * 10)) * Math.LN2, 2) + ' h (CoD ' + round(Number(regCoefExp[2]) * 100, 1) + '%)') if (Number(regCoefExp[2]) >= UserDefinedProtocol.regressionCoDMin / 100) { stepDoublingTime[stepCounter] = (1 / (Number(regCoefExp[1]) * 3600 * 10)) * Math.LN2 - theAccessory.context().put('stepCounter', ++stepCounter) - } - theExperiment.addEvent('Doubling time of the step was ' + round((1 / (Number(regCoefExp[1]) * 3600 * 10)) * Math.LN2, 2) + ' h (CoD ' + round(Number(regCoefExp[2]) * 100, 1) + '%)') - if (stepCounter >= UserDefinedProtocol.analyzedSteps) { + theAccessory.context().put('stepCounter', stepCounter + 1) + if (stepCounter >= UserDefinedProtocol.analyzedSteps) { var stepDoublingTimeAvg = 0 var stepDoublingTimeSD = 0 var stepDoublingTimeCI95 = 0 @@ -562,6 +561,7 @@ function controlPump () { } } } + } } } debugLogger('Pump max speed.') From 9eb7209d1d9d46a89544713c3b78240df5dc8d08 Mon Sep 17 00:00:00 2001 From: JanCervenyy Date: Sat, 22 May 2021 09:10:18 +0200 Subject: [PATCH 47/47] stepCounter increment fix --- PP-GrowthOptimizer.js | 100 +++++++++++++++++++++--------------------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/PP-GrowthOptimizer.js b/PP-GrowthOptimizer.js index 009c35d..b05c46f 100644 --- a/PP-GrowthOptimizer.js +++ b/PP-GrowthOptimizer.js @@ -506,62 +506,62 @@ function controlPump () { theExperiment.addEvent('Doubling time of the step was ' + round((1 / (Number(regCoefExp[1]) * 3600 * 10)) * Math.LN2, 2) + ' h (CoD ' + round(Number(regCoefExp[2]) * 100, 1) + '%)') if (Number(regCoefExp[2]) >= UserDefinedProtocol.regressionCoDMin / 100) { stepDoublingTime[stepCounter] = (1 / (Number(regCoefExp[1]) * 3600 * 10)) * Math.LN2 - theAccessory.context().put('stepCounter', stepCounter + 1) + theAccessory.context().put('stepCounter', ++stepCounter) if (stepCounter >= UserDefinedProtocol.analyzedSteps) { - var stepDoublingTimeAvg = 0 - var stepDoublingTimeSD = 0 - var stepDoublingTimeCI95 = 0 - var stepTrend = 0 - var stepAccumulated = theAccessory.context().getInt('stepAccumulated', 0) - var sumXY = 0 - var sumX = 0 - var sumY = 0 - var sumX2 = 0 - // var sumY2 = 0 - // Average of steps doubling time - for (var i = (stepCounter - 1); i >= (stepCounter - UserDefinedProtocol.analyzedSteps - stepAccumulated); i--) { - stepDoublingTimeAvg += Number(stepDoublingTime[i]) - } - stepDoublingTimeAvg /= (UserDefinedProtocol.analyzedSteps + stepAccumulated) - // CI95 of steps doubling time - for (i = (stepCounter - 1); i >= (stepCounter - UserDefinedProtocol.analyzedSteps - stepAccumulated); i--) { - stepDoublingTimeSD += Math.pow(stepDoublingTime[i] - stepDoublingTimeAvg, 2) - } - stepDoublingTimeSD = Math.sqrt(stepDoublingTimeSD / (UserDefinedProtocol.analyzedSteps + stepAccumulated - 1)) - stepDoublingTimeCI95 = stepDoublingTimeSD / Math.sqrt(UserDefinedProtocol.analyzedSteps + stepAccumulated) * 1.96 - // Trend of steps doubling time - for (i = (stepCounter - 1); i >= (stepCounter - UserDefinedProtocol.analyzedSteps - stepAccumulated); i--) { - sumX += Number(expDuration[i]) - sumX2 += Math.pow(expDuration[i], 2) - sumY += Number(stepDoublingTime[i]) - // sumY2 += Math.pow(stepDoublingTime[i], 2) - sumXY += Number(expDuration[i]) * Number(stepDoublingTime[i]) - } - stepTrend = ((UserDefinedProtocol.analyzedSteps + stepAccumulated) * sumXY - sumX * sumY) / ((UserDefinedProtocol.analyzedSteps + stepAccumulated) * sumX2 - Math.pow(sumX, 2)) * 3600 - theExperiment.addEvent('Average step doubling time is ' + round(stepDoublingTimeAvg, 2) + String.fromCharCode(177) + round(stepDoublingTimeCI95, 2) + ' h (CI95, ' + round(stepDoublingTimeCI95 / stepDoublingTimeAvg * 100, 1) + '%) with ' + round(stepTrend, 2) + ' h/h trend (' + round(stepTrend / stepDoublingTimeAvg * 100, 1) + '%)') - // Growth stability test and parameters control - if ((Math.abs(stepTrend / stepDoublingTimeAvg) <= (UserDefinedProtocol.growthTrendMax / 100)) || stepAccumulated) { + var stepDoublingTimeAvg = 0 + var stepDoublingTimeSD = 0 + var stepDoublingTimeCI95 = 0 + var stepTrend = 0 + var stepAccumulated = theAccessory.context().getInt('stepAccumulated', 0) + var sumXY = 0 + var sumX = 0 + var sumY = 0 + var sumX2 = 0 + // var sumY2 = 0 + // Average of steps doubling time + for (var i = (stepCounter - 1); i >= (stepCounter - UserDefinedProtocol.analyzedSteps - stepAccumulated); i--) { + stepDoublingTimeAvg += Number(stepDoublingTime[i]) + } + stepDoublingTimeAvg /= (UserDefinedProtocol.analyzedSteps + stepAccumulated) + // CI95 of steps doubling time + for (i = (stepCounter - 1); i >= (stepCounter - UserDefinedProtocol.analyzedSteps - stepAccumulated); i--) { + stepDoublingTimeSD += Math.pow(stepDoublingTime[i] - stepDoublingTimeAvg, 2) + } + stepDoublingTimeSD = Math.sqrt(stepDoublingTimeSD / (UserDefinedProtocol.analyzedSteps + stepAccumulated - 1)) + stepDoublingTimeCI95 = stepDoublingTimeSD / Math.sqrt(UserDefinedProtocol.analyzedSteps + stepAccumulated) * 1.96 + // Trend of steps doubling time + for (i = (stepCounter - 1); i >= (stepCounter - UserDefinedProtocol.analyzedSteps - stepAccumulated); i--) { + sumX += Number(expDuration[i]) + sumX2 += Math.pow(expDuration[i], 2) + sumY += Number(stepDoublingTime[i]) + // sumY2 += Math.pow(stepDoublingTime[i], 2) + sumXY += Number(expDuration[i]) * Number(stepDoublingTime[i]) + } + stepTrend = ((UserDefinedProtocol.analyzedSteps + stepAccumulated) * sumXY - sumX * sumY) / ((UserDefinedProtocol.analyzedSteps + stepAccumulated) * sumX2 - Math.pow(sumX, 2)) * 3600 + theExperiment.addEvent('Average step doubling time is ' + round(stepDoublingTimeAvg, 2) + String.fromCharCode(177) + round(stepDoublingTimeCI95, 2) + ' h (CI95, ' + round(stepDoublingTimeCI95 / stepDoublingTimeAvg * 100, 1) + '%) with ' + round(stepTrend, 2) + ' h/h trend (' + round(stepTrend / stepDoublingTimeAvg * 100, 1) + '%)') + // Growth stability test and parameters control + if ((Math.abs(stepTrend / stepDoublingTimeAvg) <= (UserDefinedProtocol.growthTrendMax / 100)) || stepAccumulated) { // TODO this solution removes stepTrend criteria check if at least once reached (expecting convergence!!!). May need to be upgraded later. theAccessory.context().put('stepAccumulated', stepAccumulated + 1) - if ((stabilizedTime <= experimentDuration) && ((stepDoublingTimeCI95 / stepDoublingTimeAvg) <= (UserDefinedProtocol.CI95AmplitudeMax / 100))) { - theAccessory.context().put('modeStabilized', 1) - theAccessory.context().put('stabilizedTime', experimentDuration + UserDefinedProtocol.stabilizationTimeMin * 3600) - theAccessory.context().put('stabilizedTimeMax', experimentDuration + UserDefinedProtocol.stabilizationTimeMax * 3600) - theAccessory.context().remove('stepAccumulated') - theAccessory.context().remove('stepCounter') - theAccessory.context().remove('expDuration') - theAccessory.context().remove('stepDoublingTime') - theExperiment.addEvent('*** Stabilized doubling time Dt (' + theGroup.getAccessory('thermo.thermo-reg').getValue() + ' ' + String.fromCharCode(176) + 'C, ' + theAccessory.context().getString('controlledParameterText', 'no parameter') + ') is ' + round(stepDoublingTimeAvg, 2) + String.fromCharCode(177) + round(stepDoublingTimeCI95, 2) + ' h (CI95)') - if (UserDefinedProtocol.particleSwarmOptimizer) { - PSO(stepDoublingTimeAvg) - } else if (UserDefinedProtocol.controlledParameterSteps.length > 1) { - var positions = changeParameter(UserDefinedProtocol.controlledParameter) - debugLogger('OPTIMIZER executed with fitness ' + stepDoublingTimeAvg.toFixed(2) + ' for [ ' + positions[0].toFixed(2) + ' ] and new position is [ ' + positions[1].toFixed(2) + ' ]') - theServer.sendMail('OPTIMIZER on ' + theGroup.getName() , 'NONE', ': fitness ' + stepDoublingTimeAvg.toFixed(2) + ' for [ ' + positions[0].toFixed(2) + ' ] and set new position [ ' + positions[1].toFixed(2) + ' ]') // Email notification + if ((stabilizedTime <= experimentDuration) && ((stepDoublingTimeCI95 / stepDoublingTimeAvg) <= (UserDefinedProtocol.CI95AmplitudeMax / 100))) { + theAccessory.context().put('modeStabilized', 1) + theAccessory.context().put('stabilizedTime', experimentDuration + UserDefinedProtocol.stabilizationTimeMin * 3600) + theAccessory.context().put('stabilizedTimeMax', experimentDuration + UserDefinedProtocol.stabilizationTimeMax * 3600) + theAccessory.context().remove('stepAccumulated') + theAccessory.context().remove('stepCounter') + theAccessory.context().remove('expDuration') + theAccessory.context().remove('stepDoublingTime') + theExperiment.addEvent('*** Stabilized doubling time Dt (' + theGroup.getAccessory('thermo.thermo-reg').getValue() + ' ' + String.fromCharCode(176) + 'C, ' + theAccessory.context().getString('controlledParameterText', 'no parameter') + ') is ' + round(stepDoublingTimeAvg, 2) + String.fromCharCode(177) + round(stepDoublingTimeCI95, 2) + ' h (CI95)') + if (UserDefinedProtocol.particleSwarmOptimizer) { + PSO(stepDoublingTimeAvg) + } else if (UserDefinedProtocol.controlledParameterSteps.length > 1) { + var positions = changeParameter(UserDefinedProtocol.controlledParameter) + debugLogger('OPTIMIZER executed with fitness ' + stepDoublingTimeAvg.toFixed(2) + ' for [ ' + positions[0].toFixed(2) + ' ] and new position is [ ' + positions[1].toFixed(2) + ' ]') + theServer.sendMail('OPTIMIZER on ' + theGroup.getName(), 'NONE', ': fitness ' + stepDoublingTimeAvg.toFixed(2) + ' for [ ' + positions[0].toFixed(2) + ' ] and set new position [ ' + positions[1].toFixed(2) + ' ]') // Email notification + } } } } - } } } debugLogger('Pump max speed.')