def process_chemterm(term, work, multiplier): logger.detail("Processing chemterm") terms = [] idx = 1 while idx < len(term): logger.detail('iterating term: ' + str(term[idx]) + ', ' + str(term[idx+1])) i_expr, expr, deps = process_mathterm(term[idx][1], work) unmod = expr i_unmod = i_expr mathterm = term[idx][1] # # this is a thoroughly dodgy bit of hackery # # may change in line with better expr handling if ( multiplier != 1 ): expr = '(' + str(multiplier) + '*' + expr + ')' i_expr = (('literal','(' + str(multiplier) + '*'),) + i_expr + (('literal',')'),) mathterm = ('arithmetic', '*', ('mathterm', decimal.Decimal('-1')), term[idx]) chem = process_chemical(term[idx+1], work) terms.append({'stoich': expr, 'chem':chem, 'unmod':unmod, 'i_stoich': i_expr, 'i_unmod':i_unmod, 'mathterm_unmod':term[idx][1], 'mathterm':mathterm, 'depends': deps|work['symbols'][chem]['depends']}) idx = idx + 2 return terms
def process_explicit_rate(term, work, lhs): logger.detail("Processing explicit rateterm") if len(term[2]) > 2: logger.warn("More than 1 rate term supplied, ignoring excess") i_expr, expr, deps = process_mathterm(term[2][1][1], work) return { 'i_expr':i_expr, 'expr':expr, 'depends':deps, 'mathterm':term[2][1][1] }
def process_flux(term, work): tag = term[1] logger.detail('Processing ' + tag + ' reaction') label = term[2][1] if label == '': label = default_label(tag + '__') while label in work['reactions'].keys(): newlabel = default_label(tag + '__') logger.warn("Duplicate reaction label '" + label + "', substituting '" + newlabel + "'") label = newlabel # I have no idea whether this is reasonable, but: # outflux reactions have an LHS and can (if necessary) # be given MA rates; influx reactions don't and can't if tag == 'outflux': terms = process_chemterm(term[3], work, -1) rate = process_rateterm(term[4], work, terms) lhs = terms rhs = [] else: terms = process_chemterm(term[3], work, 1) rate = process_rateterm(term[4], work, None) lhs = [] rhs = terms work['reactions'][label] = { 'type' : tag, 'terms' : terms, 'rate' : rate, 'lhs' : lhs, 'rhs': rhs, 'ratespec': term[4] } consolidate_chems(work['reactions'][label])
def process_flux(term, work): tag = term[1] logger.detail('Processing ' + tag + ' reaction') label = term[2][1] if label == '': label = default_label(tag + '__') while label in work['reactions'].keys(): newlabel = default_label(tag + '__') logger.warn("Duplicate reaction label '" + label + "', substituting '" + newlabel + "'") label = newlabel # I have no idea whether this is reasonable, but: # outflux reactions have an LHS and can (if necessary) # be given MA rates; influx reactions don't and can't if tag == 'outflux': terms = process_chemterm(term[3], work, -1) rate = process_rateterm(term[4], work, terms) lhs = terms rhs = [] else: terms = process_chemterm(term[3], work, 1) rate = process_rateterm(term[4], work, None) lhs = [] rhs = terms work['reactions'][label] = { 'type': tag, 'terms': terms, 'rate': rate, 'lhs': lhs, 'rhs': rhs, 'ratespec': term[4] } consolidate_chems(work['reactions'][label])
def process_chemterm(term, work, multiplier): logger.detail("Processing chemterm") terms = [] idx = 1 while idx < len(term): logger.detail('iterating term: ' + str(term[idx]) + ', ' + str(term[idx + 1])) i_expr, expr, deps = process_mathterm(term[idx][1], work) unmod = expr i_unmod = i_expr mathterm = term[idx][1] # # this is a thoroughly dodgy bit of hackery # # may change in line with better expr handling if (multiplier != 1): expr = '(' + str(multiplier) + '*' + expr + ')' i_expr = (('literal', '(' + str(multiplier) + '*'), ) + i_expr + ( ('literal', ')'), ) mathterm = ('arithmetic', '*', ('mathterm', decimal.Decimal('-1')), term[idx]) chem = process_chemical(term[idx + 1], work) terms.append({ 'stoich': expr, 'chem': chem, 'unmod': unmod, 'i_stoich': i_expr, 'i_unmod': i_unmod, 'mathterm_unmod': term[idx][1], 'mathterm': mathterm, 'depends': deps | work['symbols'][chem]['depends'] }) idx = idx + 2 return terms
def process_explicit_rate(term, work, lhs): logger.detail("Processing explicit rateterm") if len(term[2]) > 2: logger.warn("More than 1 rate term supplied, ignoring excess") i_expr, expr, deps = process_mathterm(term[2][1][1], work) return { 'i_expr': i_expr, 'expr': expr, 'depends': deps, 'mathterm': term[2][1][1] }
def process_twoway(term, work): label = term[2][1] logger.detail("Processing twoway reaction '" + label + "'") if label == '': label = default_label('twoway__') forward = ('reaction', 'oneway', ('label', label + '_forward'), term[3], term[4], term[5]) reverse = ('reaction', 'oneway', ('label', label + '_reverse'), term[4], term[3], term[6]) process_oneway(forward, work) process_oneway(reverse, work)
def process_assign(item, work): target = item[1] logger.detail("Processing assignment to variable: " + target) symbol = declare_symbol(target, work) if work['docstack']: symbol['docs'].extend(work['docstack']) work['docstack'] = [] work['assigned'].add(target) i_expr, expr, depends = process_mathterm(item[2][1], work) symbol['assigns'].append({'expr':expr, 'i_expr':i_expr, 'depends':depends, 'init':item[4], 'mathterm':item[2][1]}) symbol['depends'] |= depends
def load_sources(config): sources = config['sources'] srcIndex = 0 parsedSources = [] failedSources = [] merged = [] while srcIndex < len(sources): logger.message("Searching for source file: " + sources[srcIndex]) src = search_file(sources[srcIndex], config['modelpath']) if (src is None) and (not sources[srcIndex].endswith(MODELDEF_EXT)): logger.message("Not found, trying with added extension: " + sources[srcIndex] + MODELDEF_EXT) src = search_file(sources[srcIndex] + MODELDEF_EXT, config['modelpath']) if src is None: logger.warn("File not found: " + sources[srcIndex]) failedSources.append(sources[srcIndex]) else: nErrs, ast = parse_file(src) if nErrs > 0 or ast is None: failedSources.append(src) else: ast = list(ast) # add imports that are not already in the source list to it for imp in list( sum([x[1:] for x in ast if x[0] == 'import'], ())): if imp not in sources and imp + MODELDEF_EXT not in sources: sources.append(imp) logger.detail(ast, prettify=True) parsedSources.append((sources[srcIndex], src)) merged = merged + ast srcIndex = srcIndex + 1 logger.message("Total number of attempted source files: %d" % srcIndex) logger.message("%d parsed, %d failed" % (len(parsedSources), len(failedSources))) for failed in failedSources: logger.message(" -> %s" % failed) return { 'sources': sources, 'parsed': parsedSources, 'failed': failedSources, 'merged': merged }
def process_mathterm(term, work): logger.detail("Processing mathterm: " + str(term)) if isinstance(term, decimal.Decimal): return (('literal', str(float(term))), ), str(term), set() if isinstance(term, str): declare_symbol(term, work) return (('symbol', term), ), term, set([term]) if not isinstance(term, tuple): return (('error', 'ERROR'), ), 'ERROR', set() return { 'function': process_function, 'conditional': process_conditional, 'arithmetic': process_binop }.get(term[0], unknown_mathterm)(term, work)
def process_mathterm(term, work): logger.detail("Processing mathterm: " + str(term)) if isinstance(term, decimal.Decimal): return (('literal', str(float(term))),), str(term), set() if isinstance(term, str): declare_symbol(term,work) return (('symbol', term),), term, set([term]) if not isinstance(term, tuple): return (('error','ERROR'),), 'ERROR', set() return { 'function' : process_function, 'conditional' : process_conditional, 'arithmetic' : process_binop }.get(term[0], unknown_mathterm)(term, work)
def recurse_dependencies(name, parents, done, symbols): if name in parents: if not symbols[name]['circular']: logger.detail('Circular dependency found for ' + name) symbols[name]['circular'] = True elif symbols[name]['circular']: logger.detail('Previous circularity noted for ' + name) else: for dep in symbols[name]['depends']: if dep in done: symbols[name]['depends'] = symbols[name]['depends'] | symbols[dep]['depends'] else: symbols[name]['depends'] = (symbols[name]['depends'] | recurse_dependencies(dep, parents | set([name]), done, symbols)) done = done | set([name]) return symbols[name]['depends']
def process_chemical(term, work): logger.detail("Processing chemical: " + str(term)) if len(term) == 2: chem = term[1] work['chemicals'][chem] = None symbol = declare_symbol(chem, work) else: chem = term[1] + '_' + term[2] work['chemicals'][chem] = term[2] declare_symbol(term[2], work) symbol = declare_symbol(chem, work) symbol['depends'] = symbol['depends'] | set([term[2]]) # automatic non-negativity constraint on chemicals if NON_NEGATIVE not in symbol['constraints']: symbol['constraints'].append(NON_NEGATIVE) return chem
def process_assign(item, work): target = item[1] logger.detail("Processing assignment to variable: " + target) symbol = declare_symbol(target, work) if work['docstack']: symbol['docs'].extend(work['docstack']) work['docstack'] = [] work['assigned'].add(target) i_expr, expr, depends = process_mathterm(item[2][1], work) symbol['assigns'].append({ 'expr': expr, 'i_expr': i_expr, 'depends': depends, 'init': item[4], 'mathterm': item[2][1] }) symbol['depends'] |= depends
def declare_symbol(name, work): if name in work['symbols']: symbol = work['symbols'][name] else: symbol = { 'id' : name, 'depends' : set(), 'conflicts':0, 'assigns':[], 'diffs':[], 'algs':[], 'constraints':[], 'circular': False, 'index':len(work['symbols']), 'docs':[], 'tags':[] } work['symbols'][name] = symbol work['symlist'].append(name) logger.detail("Created symbol '" + name + "' at index %d" % work['symbols'][name]['index']) return symbol
def load_sources(config): sources = config['sources'] srcIndex = 0 parsedSources = [] failedSources = [] merged = [] while srcIndex < len(sources): logger.message("Searching for source file: " + sources[srcIndex]) src = search_file(sources[srcIndex], config['modelpath']) if ( src is None ) and ( not sources[srcIndex].endswith(MODELDEF_EXT) ): logger.message("Not found, trying with added extension: " + sources[srcIndex] + MODELDEF_EXT) src = search_file(sources[srcIndex] + MODELDEF_EXT, config['modelpath']) if src is None: logger.warn("File not found: " + sources[srcIndex]) failedSources.append(sources[srcIndex]) else: nErrs, ast = parse_file(src) if nErrs > 0 or ast is None: failedSources.append(src) else: ast = list(ast) # add imports that are not already in the source list to it for imp in list(sum([x[1:] for x in ast if x[0]=='import'], ())): if imp not in sources and imp + MODELDEF_EXT not in sources: sources.append(imp) logger.detail(ast, prettify=True) parsedSources.append((sources[srcIndex],src)) merged = merged + ast srcIndex = srcIndex + 1 logger.message("Total number of attempted source files: %d" % srcIndex) logger.message("%d parsed, %d failed" % (len(parsedSources), len(failedSources))) for failed in failedSources: logger.message(" -> %s" % failed) return {'sources':sources, 'parsed':parsedSources, 'failed':failedSources, 'merged':merged}
def recurse_dependencies(name, parents, done, symbols): if name in parents: if not symbols[name]['circular']: logger.detail('Circular dependency found for ' + name) symbols[name]['circular'] = True elif symbols[name]['circular']: logger.detail('Previous circularity noted for ' + name) else: for dep in symbols[name]['depends']: if dep in done: symbols[name]['depends'] = symbols[name]['depends'] | symbols[ dep]['depends'] else: symbols[name]['depends'] = (symbols[name]['depends'] | recurse_dependencies( dep, parents | set([name]), done, symbols)) done = done | set([name]) return symbols[name]['depends']
def process_oneway(term, work): label = term[2][1] logger.detail("Processing oneway reaction '" + label + "'") if label == '': label = default_label('oneway__') while label in work['reactions'].keys(): newlabel = default_label('oneway__') logger.warn("Duplicate reaction label '" + label + "', substituting '" + newlabel + "'") label = newlabel lhs = process_chemterm(term[3], work, -1) rhs = process_chemterm(term[4], work, 1) rate = process_rateterm(term[5], work, lhs) work['reactions'][label] = { 'type' : 'oneway', 'terms' : lhs + rhs, 'rate' : rate, 'lhs' : lhs, 'rhs' : rhs, 'ratespec': term[5] } consolidate_chems(work['reactions'][label])
def declare_symbol(name, work): if name in work['symbols']: symbol = work['symbols'][name] else: symbol = { 'id': name, 'depends': set(), 'conflicts': 0, 'assigns': [], 'diffs': [], 'algs': [], 'constraints': [], 'circular': False, 'index': len(work['symbols']), 'docs': [], 'tags': [] } work['symbols'][name] = symbol work['symlist'].append(name) logger.detail("Created symbol '" + name + "' at index %d" % work['symbols'][name]['index']) return symbol
def process_oneway(term, work): label = term[2][1] logger.detail("Processing oneway reaction '" + label + "'") if label == '': label = default_label('oneway__') while label in work['reactions'].keys(): newlabel = default_label('oneway__') logger.warn("Duplicate reaction label '" + label + "', substituting '" + newlabel + "'") label = newlabel lhs = process_chemterm(term[3], work, -1) rhs = process_chemterm(term[4], work, 1) rate = process_rateterm(term[5], work, lhs) work['reactions'][label] = { 'type': 'oneway', 'terms': lhs + rhs, 'rate': rate, 'lhs': lhs, 'rhs': rhs, 'ratespec': term[5] } consolidate_chems(work['reactions'][label])
def process_extern(item, work): logger.detail('Appending extern fields:' + str(item[1:])) # only include each field once work['extern'].extend([x for x in item[1:] if x not in work['extern']])
def process_input(item, work): logger.detail('Appending input fields:' + str(item[1:])) # only include each field once work['inputs'].extend([x for x in item[1:] if x not in work['inputs']])
def process_output(item, work): logger.detail('Appending default output fields:' + str(item[1:])) # only include each field once for id in item[1:]: if id not in work['outputs']: work['outputs'].append(id)
def ignore_item(item, work): logger.detail("Ignoring item: " + item[0])
def process_embedded(item, work): logger.detail("Processing embedded code fragment") work['embeds'].append(item[1])
def process_MA_rate(term, work, lhs): logger.detail("Processing mass action rateterm") if len(term[2]) > 2: logger.detail( "More than 1 rate term supplied, extras will be taken as concentration exponents" ) i_expr, expr, deps = process_mathterm(term[2][1][1], work) mathterm = term[2][1][1] if lhs is None: logger.detail("Reaction has no LHS, omitting concentration dependence") else: num = [] denom = [] i_num = [] i_denom = [] m_num = () m_denom = () offset_exp_idx = 2 for chem in lhs: name = chem['chem'] if len(term[2]) > offset_exp_idx: i_exponent, exponent, exp_deps = process_mathterm( term[2][offset_exp_idx][1], work) m_exponent = term[2][offset_exp_idx][1] num.append('pow(' + name + ',' + exponent + ')') i_num.append((('literal', 'pow('), ) + (('symbol', name), ) + (('literal', ','), ) + i_exponent + (('literal', ')'), )) deps = deps | exp_deps m_sub = ('arithmetic', '^', ('mathterm', name), ('mathterm', m_exponent)) if m_num: m_num = ('arithmetic', '*', ('mathterm', m_num), ('mathterm', m_sub)) else: m_num = m_sub else: num.append(name) i_num.append((('symbol', name), )) if m_num: m_num = ('arithmetic', '*', ('mathterm', m_num), ('mathterm', name)) else: m_num = name if work['chemicals'][name] is not None: compartment = work['chemicals'][name] denom.append(compartment) i_denom.append((('symbol', compartment), )) if m_denom: m_denom = ('arithmetic', '*', ('mathterm', m_denom), ('mathterm', compartment)) else: m_denom = compartment deps = deps | set([name]) | work['symbols'][name]['depends'] offset_exp_idx += 1 factor = '(' + '*'.join(num) + ')' i_factor = i_num[0] for ifx in i_num[1:]: i_factor = i_factor + (('literal', '*'), ) + ifx i_factor = (('literal', '('), ) + i_factor + (('literal', ')'), ) m_factor = m_num if len(denom) > 0: factor = factor + '/(' + '*'.join(denom) + ')' i_divisor = i_denom[0] for idn in i_denom[1:]: i_divisor = i_divisor + (('literal', '*'), ) + idn i_factor = i_factor + (('literal', '/('), ) + i_divisor + ( ('literal', ')'), ) m_factor = ('arithmetic', '/', ('mathterm', m_factor), ('mathterm', m_denom)) expr = '(' + expr + '*' + factor + ')' i_expr = (('literal','('),) + i_expr + (('literal','*'),) \ + i_factor + (('literal',')'),) mathterm = ('arithmetic', '+', ('mathterm', mathterm), ('mathterm', m_factor)) return { 'i_expr': i_expr, 'expr': expr, 'depends': deps, 'mathterm': mathterm }
def process_MA_rate(term, work, lhs): logger.detail("Processing mass action rateterm") if len(term[2]) > 2: logger.detail("More than 1 rate term supplied, extras will be taken as concentration exponents") i_expr, expr, deps = process_mathterm(term[2][1][1], work) mathterm = term[2][1][1] if lhs is None: logger.detail("Reaction has no LHS, omitting concentration dependence") else: num = [] denom = [] i_num = [] i_denom = [] m_num = () m_denom = () offset_exp_idx = 2 for chem in lhs: name = chem['chem'] if len(term[2]) > offset_exp_idx: i_exponent, exponent, exp_deps = process_mathterm(term[2][offset_exp_idx][1], work) m_exponent = term[2][offset_exp_idx][1] num.append('pow(' + name + ',' + exponent + ')') i_num.append( (('literal','pow('),) + (('symbol', name),) + (('literal',','),) + i_exponent + (('literal',')'),) ) deps = deps | exp_deps m_sub = ('arithmetic', '^', ('mathterm', name), ('mathterm', m_exponent)) if m_num: m_num = ('arithmetic', '*', ('mathterm', m_num), ('mathterm', m_sub)) else: m_num = m_sub else: num.append(name) i_num.append((('symbol',name),)) if m_num: m_num = ('arithmetic', '*', ('mathterm', m_num), ('mathterm', name)) else: m_num = name if work['chemicals'][name] is not None: compartment = work['chemicals'][name] denom.append(compartment) i_denom.append((('symbol',compartment),)) if m_denom: m_denom = ('arithmetic', '*', ('mathterm', m_denom), ('mathterm', compartment)) else: m_denom = compartment deps = deps | set([name]) | work['symbols'][name]['depends'] offset_exp_idx += 1 factor = '(' + '*'.join(num) + ')' i_factor = i_num[0] for ifx in i_num[1:]: i_factor = i_factor + (('literal','*'),) + ifx i_factor = (('literal','('),) + i_factor + (('literal',')'),) m_factor = m_num if len(denom) > 0: factor = factor + '/(' + '*'.join(denom) + ')' i_divisor = i_denom[0] for idn in i_denom[1:]: i_divisor = i_divisor + (('literal','*'),) + idn i_factor = i_factor + (('literal','/('),) + i_divisor + (('literal', ')'),) m_factor = ('arithmetic', '/', ('mathterm', m_factor), ('mathterm', m_denom)) expr = '(' + expr + '*' + factor + ')' i_expr = (('literal','('),) + i_expr + (('literal','*'),) \ + i_factor + (('literal',')'),) mathterm = ('arithmetic', '+', ('mathterm', mathterm), ('mathterm', m_factor)) return { 'i_expr':i_expr, 'expr':expr, 'depends':deps, 'mathterm':mathterm }
def process_MM_rate(term, work, lhs): logger.detail("Processing Michaelis-Menten rateterm") arglist = term[2] if len(arglist) != len(lhs) + 2: # one for 'arglist' + one for Vmax logger.warn("Incorrect parameters for Michaelis-Menten rate term, skipping!") return { 'i_expr':('error','FAILED'), 'expr':'FAILED', 'depends':set() } i_num = [] i_denom = [] num = [] denom = [] i_expr, expr, deps = process_mathterm(arglist[1][1], work) num.append(expr) i_num.append(i_expr) m_num = arglist[1][1] m_denom = () for idx in range(len(lhs)): logger.detail(idx) i_Km_expr, Km_expr, km_deps = process_mathterm(arglist[idx+2][1], work) deps = deps | km_deps | lhs[idx]['depends'] m_Km = arglist[idx+2][1] # explicitly add Km to dependencies if it is a symbol in its own right # since otherwise the dependency won't get registered if Km_expr in work['symbols'].keys(): deps = deps | set([Km_expr]) chem = lhs[idx]['chem'] i_chem = (('symbol',chem),) stoich = lhs[idx]['unmod'] # we only want the original value without the -1 multiplier i_stoich = lhs[idx]['i_unmod'] m_stoich = lhs[idx]['mathterm_unmod'] # x^1 is obviously just x... if stoich == '1': conc_pwr = chem Km_pwr = Km_expr i_conc_pwr = i_chem i_Km_pwr = i_Km_expr m_conc_pwr = chem m_Km_pwr = m_Km else: conc_pwr = 'pow(' + chem + ',' + stoich + ')' Km_pwr = 'pow(' + Km_expr + ',' + stoich + ')' i_conc_pwr = (('literal','pow('),) + i_chem + (('literal',','),) + i_stoich + (('literal',')'),) i_Km_pwr = (('literal','pow('),) + i_Km_expr + (('literal',','),) + i_stoich + (('literal',')'),) m_conc_pwr = ('arithmetic', '^', ('mathterm', chem), ('mathterm', m_stoich)) m_Km_pwr = ('arithmetic', '^', ('mathterm', m_Km), ('mathterm', m_stoich)) num.append(conc_pwr) denom.append('(' + Km_pwr + '+' + conc_pwr + ')') i_num.append(i_conc_pwr) i_denom.append((('literal', '('),) + i_Km_pwr + (('literal','+'),) + i_conc_pwr + (('literal',')'),)) m_num = ('arithmetic', '*', ('mathterm', m_num), ('mathterm', m_conc_pwr)) m_sub = ('arithmetic', '+', ('mathterm', m_Km_pwr), ('mathterm', m_conc_pwr)) if m_denom: m_denom = ('arithmetic', '*', ('mathterm', m_denom), ('mathterm', m_sub)) else: m_denom = m_sub num_expr = '*'.join(num) denom_expr = '*'.join(denom) expr = '((' + num_expr + ')/(' + denom_expr + '))' i_num_expr = i_num[0] for iex in i_num[1:]: i_num_expr = i_num_expr + (('literal','*'),) + iex i_denom_expr = i_denom[0] for iex in i_denom[1:]: i_denom_expr = i_denom_expr + (('literal','*'),) + iex i_expr = (('literal','(('),) + i_num_expr + (('literal',')/('),) \ + i_denom_expr + (('literal','))'),) mathterm = ('arithmetic', '/', ('mathterm', m_num), ('mathterm', m_denom)) return { 'i_expr':i_expr, 'expr':expr, 'depends':deps, 'mathterm': mathterm }
def process_MM_rate(term, work, lhs): logger.detail("Processing Michaelis-Menten rateterm") arglist = term[2] if len(arglist) != len(lhs) + 2: # one for 'arglist' + one for Vmax logger.warn( "Incorrect parameters for Michaelis-Menten rate term, skipping!") return { 'i_expr': ('error', 'FAILED'), 'expr': 'FAILED', 'depends': set() } i_num = [] i_denom = [] num = [] denom = [] i_expr, expr, deps = process_mathterm(arglist[1][1], work) num.append(expr) i_num.append(i_expr) m_num = arglist[1][1] m_denom = () for idx in range(len(lhs)): logger.detail(idx) i_Km_expr, Km_expr, km_deps = process_mathterm(arglist[idx + 2][1], work) deps = deps | km_deps | lhs[idx]['depends'] m_Km = arglist[idx + 2][1] # explicitly add Km to dependencies if it is a symbol in its own right # since otherwise the dependency won't get registered if Km_expr in work['symbols'].keys(): deps = deps | set([Km_expr]) chem = lhs[idx]['chem'] i_chem = (('symbol', chem), ) stoich = lhs[idx][ 'unmod'] # we only want the original value without the -1 multiplier i_stoich = lhs[idx]['i_unmod'] m_stoich = lhs[idx]['mathterm_unmod'] # x^1 is obviously just x... if stoich == '1': conc_pwr = chem Km_pwr = Km_expr i_conc_pwr = i_chem i_Km_pwr = i_Km_expr m_conc_pwr = chem m_Km_pwr = m_Km else: conc_pwr = 'pow(' + chem + ',' + stoich + ')' Km_pwr = 'pow(' + Km_expr + ',' + stoich + ')' i_conc_pwr = (('literal', 'pow('), ) + i_chem + ( ('literal', ','), ) + i_stoich + (('literal', ')'), ) i_Km_pwr = (('literal', 'pow('), ) + i_Km_expr + ( ('literal', ','), ) + i_stoich + (('literal', ')'), ) m_conc_pwr = ('arithmetic', '^', ('mathterm', chem), ('mathterm', m_stoich)) m_Km_pwr = ('arithmetic', '^', ('mathterm', m_Km), ('mathterm', m_stoich)) num.append(conc_pwr) denom.append('(' + Km_pwr + '+' + conc_pwr + ')') i_num.append(i_conc_pwr) i_denom.append((('literal', '('), ) + i_Km_pwr + (('literal', '+'), ) + i_conc_pwr + (('literal', ')'), )) m_num = ('arithmetic', '*', ('mathterm', m_num), ('mathterm', m_conc_pwr)) m_sub = ('arithmetic', '+', ('mathterm', m_Km_pwr), ('mathterm', m_conc_pwr)) if m_denom: m_denom = ('arithmetic', '*', ('mathterm', m_denom), ('mathterm', m_sub)) else: m_denom = m_sub num_expr = '*'.join(num) denom_expr = '*'.join(denom) expr = '((' + num_expr + ')/(' + denom_expr + '))' i_num_expr = i_num[0] for iex in i_num[1:]: i_num_expr = i_num_expr + (('literal', '*'), ) + iex i_denom_expr = i_denom[0] for iex in i_denom[1:]: i_denom_expr = i_denom_expr + (('literal', '*'), ) + iex i_expr = (('literal','(('),) + i_num_expr + (('literal',')/('),) \ + i_denom_expr + (('literal','))'),) mathterm = ('arithmetic', '/', ('mathterm', m_num), ('mathterm', m_denom)) return { 'i_expr': i_expr, 'expr': expr, 'depends': deps, 'mathterm': mathterm }
def logModelInfo(model, config): logger.detail('\n\n** work **\n', False) logger.detail(model) # log the main actual equation system logger.message("\n\n** equations **") for name in model['diffs']: sym = model['symbols'][name] if sym['conflicts'] > 0: tag = '[CONFLICT-?] ' else: tag = '' lhs = name + "'" for aux in model['auxiliaries'][name]: mass = aux[0] if mass < 0: mass = mass * -1 op = ' - ' else: op = ' + ' lhs = lhs + op + str(mass) + ' ' + aux[1] + "'" for diff in sym['diffs']: logger.message(tag + lhs + ' = ' + diff['expr']) for name in model['algs']: sym = model['symbols'][name] if sym['conflicts'] > 0: tag = '[CONFLICT-?] ' else: tag = '' for alg in sym['algs']: logger.message(tag + 'f(' + name + ') : 0 = ' + alg['expr']) # log dependency info logger.message("\n\n** dependency analysis **") logger.message("\nThe following solver variables are used in the model:") for name in sorted(model['diffs'], key=str.lower): logger.message(name) for name in sorted(model['algs'], key=str.lower): logger.message(name) logger.detail("\nThe following intermediate variables or parameters are used by the model:", False) for name in sorted(model['required'], key=str.lower): if name in model['assigned']: logger.detail(name + ' (assigned)') else: logger.detail(name + ' (unassigned)') if model['inputs']: logger.message('\nThe following symbols are declared as inputs:') for name in sorted(model['inputs'], key=str.lower): logger.message(name) if model['params']: logger.message('\nThe following symbols are parameters, independent of the solver variables:') for name in sorted(model['params'], key=str.lower): logger.message(name) logger.message('\nThe following parameters have no dependencies at all:') for name in sorted(model['params'], key=str.lower): if len(model['symbols'][name]['depends']) == 0: logger.message(name) if model['intermeds']: logger.message('\nThe following symbols are intermediates, with solver variable dependencies:') for name in sorted(model['intermeds'], key=str.lower): logger.message(name) if model['unused']: logger.message('\nThe following intermediate variables or parameters are declared but unused:') for name in sorted(model['unused'], key=str.lower): logger.message(name) if config['unused']: logger.message('(NB: unused variables will still be calculated)') else: logger.message('(NB: unused variables will NOT be calculated)') undoc = [ x for x in model['symbols'] if not [y for y in model['symbols'][x]['docs'] if not y.startswith('+')] ] if undoc: logger.message('\nThe following symbols are not documented:') for name in sorted(undoc, key=str.lower): logger.message(name) unassigned = set(model['symlist']) - model['assigned'] if unassigned: logger.warn('\nThe following symbols are never explicitly assigned (will default to 0):') for name in sorted(unassigned, key=str.lower): logger.warn(name) if model['extern']: logger.warn('\nThe following external dependencies are declared but unsatisfied:\n') for name in sorted(model['extern'], key=str.lower): logger.warn(name) if model['unknown']: logger.warn("\nThe model makes use of the following non-standard functions:") for name in model['unknown']: logger.warn(name) # examine circular dependencies logger.detail("\nCircular dependencies:") for name in model['symbols'].keys(): if model['symbols'][name]['circular']: if name in model['roots']: logger.detail(name + " (is a solver var)") elif name in model['unused']: logger.detail(name + ' (unused)') else: LHS = model['symbols'][name]['depends'] & set(model['roots']) if len(LHS) == 0: logger.detail(name + " (no LHS dependencies)") else: logger.detail(name + ' ' + str(LHS)) logger.message('') logger.message('** summary for model %s **' % config['name']) logger.message('%d model variables (%d differential, %d algebraic)' % (len(model['roots']), len(model['diffs']), len(model['algs']))) logger.message('%d intermediate variables (%d unused)' % (len(model['intermeds']), len([x for x in model['intermeds'] if x in model['unused']]))) logger.message('%d parameters (%d unused)' % (len(model['params']), len([x for x in model['params'] if x in model['unused']]))) logger.message('%d unsatisfied external dependencies\n' % len(model['extern'])) logger.message('')