def Solve(self): """ determine variable to solve for and call appropriate method (supplied by derived class) """ p0 = self.GetPort(IN_PORT_0) p1 = self.GetPort(IN_PORT_1) pOut = self.GetPort(OUT_PORT) value0 = p0.GetValue() value1 = p1.GetValue() result = pOut.GetValue() if value0 != None: if value1 != None: try: result = self.CalcResult(value0, value1) except ValueError: return pOut.SetValue(result, CALCULATED_V) elif result != None: try: value1 = self.CalcValue1(value0, result) except ValueError: return p1.SetValue(value1, CALCULATED_V) elif value1 != None and result != None: try: value0 = self.CalcValue0(value1, result) except ValueError: raise Error.SimError('EqnCalcError', self.GetPath()) p0.SetValue(value0, CALCULATED_V)
def CalcJacobian(self): """ Use crude numerical differences to approximate Jacobian return inverse """ jacobian = zeros((self.numberActive, self.numberActive), Float) for cont in self.activeControllers: cont.SaveBase() self.errors = self.GetErrors() delta = 0.1 for i in range(self.numberActive): self.flowsheet.InfoMessage('ContDerivCalc', (self.flowsheet.GetPath(), i)) cont = self.activeControllers[i] cont.SetOutput(delta) self.flowsheet.InnerSolve() jacobian[:,i] = (self.GetErrors() - self.errors)/delta cont.SetOutput(0.0) self.flowsheet.InnerSolve() try: self.jacobian = inverse(jacobian) except: raise Error.SimError('CouldNotInvertJacobian', self.flowsheet.GetPath())
def ProcessParen(self): """ work back up operator stack until matching '(' is found """ while 1: if len(self.operatorStack) == 0: raise Error.SimError(EqnParenMismatch, (self.currentEqn, self.GetPath())) op = self.operatorStack[-1] if op == '(': self.operatorStack.pop() # just throw away matching paren return else: self.ProcessTopOperator()
def ParseEquation(self, tokens): """ tokens is list of tokens """ if len(tokens) == 0: return self.operatorStack = ['('] # start with paren to make end of input easy self.operandStack = [] for token in tokens: # find first operand if token == '(': self.operatorStack.append(token) elif token == ')': self.ProcessParen() elif token in _operators: opClass = _operators[token] op = opClass() self.AddObject(op, '%s_%d' % (opClass.__name__, self.opCount)) self.installedOps.append(op) self.opCount += 1 prevOp = self.operatorStack[-1] while prevOp != '(' and prevOp.precedence >= op.precedence: self.ProcessTopOperator() prevOp = self.operatorStack[-1] self.operatorStack.append(op) elif token in self.GetPortNames(SIG): signal = self.GetChildUO(MakeSignalName(token)) self.operandStack.append(signal) else: # should be a number try: value = float(token) except ValueError: raise Error.SimError('EqnUnknownToken', (token, self.currentEqn, self.GetPath())) self.operandStack.append(value) # process everything back to that initial paren self.ProcessParen() if len(self.operandStack) > 1 or len(self.operatorStack): self.SyntaxError()
def ParseFormula(self): eqnStr = self.parameters[RXNFFORMULA_PAR] eqn = eqnStr # keep a copy of the original equation if (eqnStr == None or eqnStr == ''): return # reset all coeffs to zero cmpNames = self.GetCompoundNames() self.cmpNames = cmpNames self.stoichCoeffs = [] for i in range(len(cmpNames)): self.stoichCoeffs.append(0) # replace compounds within quotes by the index # for compounds with '-' cmps = re.findall(r'"[^"]+"|\'[^\']+\'', eqnStr) for token in cmps: # strip out the quote cmp = token[1:-1] try: idx = self.CompoundIndex(cmp) #eqnStr = re.sub(token, str(idx), eqnStr) eqnStr = eqnStr.replace(token, str(idx)) except: pass try: # extract the reaction name tokens = re.split(r'\:', eqnStr, 1) if (len(tokens) == 2): eqnStr = string.strip(tokens[1]) self.rxnName = string.strip(tokens[0]) # replace all - by +- so that when i split the tokens, # the signs of the coeff are preserved eqnStr = re.sub('\-', '+-', eqnStr) tokens = re.split('\+', eqnStr) for token in tokens: if (string.strip(token) == ''): continue x = re.split('\*', string.strip(token)) # if coeff is missing, assume 1 or -1 if len(x) == 1: x0 = x[0] if x0[0] == '-': x.append(x0[1:]) x[0] = '-1' else: x.append(x0) x[0] = '1' # let underscores stand for spaces cmp = re.sub('_', ' ', string.strip(x[1])) # base compound indicator baseCmp = 0 if (cmp[0] == '!'): cmp = cmp[1:] baseCmp = 1 # if the input compound name is numeric, it is the compound index try: idx = int(cmp) except: idx = cmpNames.index(cmp) coef = float(string.strip(x[0])) self.stoichCoeffs[idx]= coef if baseCmp: self.baseCompIdx = idx except: #self.SetParameterValue(RXNFFORMULA_PAR, '') self.stoichCoeffs = [] raise Error.SimError('EqnSyntax', (eqn, self.GetPath())) # base compound must be a reactant # check for equation mass balance later (need MW of selected compounds) if self.baseCompIdx < 0: raise Error.SimError('EqnSyntax', (eqn, self.GetPath())) elif self.stoichCoeffs[self.baseCompIdx] >= 0: raise Error.SimError('EqnSyntax', (eqn, self.GetPath()))
def SyntaxError(self): raise Error.SimError('EqnSyntax', (self.currentEqn, self.GetPath()))
def SetParameterValue(self, name, value): """ do the main work of parsing the equation and setting up the solution network """ super(Equation, self).SetParameterValue(name, value) if name == EQUATION_PAR: # eliminate the old operators for op in self.installedOps: self.DeleteObject(op) self.installedOps = [] self.opCount = 0 # remove any clone ports from installed signal streams for name in self.GetPortNames(SIG): sig = self.GetChildUO(MakeSignalName(name)) if sig: nTimesUsed = sig.GetParameterValue(USEDCOUNT_PAR) for i in range(1, nTimesUsed): sig.DeletePortNamed('Clone_%d' % i) sig.SetParameterValue(USEDCOUNT_PAR, 0) lines = re.split(r'\n', value) signals = [] eqns = [] for line in lines: line = string.strip(line) if _reSignal.match(line): signals.append(line) else: eqns.append(line) newNames = [] for sigDcl in signals: # step over word signal sigDcl = string.lstrip(sigDcl[6:]) dclTypes = _reTypeDcl.findall(sigDcl) for dclType in dclTypes: (sigType, sigNames, junk) = _reEitherParen.split(dclType) sigNames = _reSpaceComma.split(sigNames) for name in sigNames: if not name: continue if name in newNames: raise Error.SimError('EqnDuplicateSigName', (name, self.GetPath())) newNames.append(name) if name not in self.GetPortNames(SIG): stream = Stream.Stream_Signal() stream.SetParameterValue(SIGTYPE_PAR, sigType) stream.SetParameterValue(USEDCOUNT_PAR, 0) self.AddObject(stream, MakeSignalName(name)) self.BorrowChildPort(stream.GetPort(IN_PORT), name) # any current ports not in new list need to be removed missingNames = [] for name in self.GetPortNames(SIG): if name not in newNames: missingNames.append(name) for name in missingNames: stream = self.GetObject(MakeSignalName(name)) port = self.GetPort(name) self.DelUnitOperation(MakeSignalName(portName)) self.DeleteObject(port) # now parse the equations for eqn in eqns: # transform string into list of tokens if not eqn: continue tokens = _reTokenizeEqn.findall(eqn) self.currentEqn = eqn # for error reporting self.ParseEquation(tokens)