4343 ReactionStatement ,
4444 )
4545
46+ def _int_or_double (c ):
47+ return (
48+ Integer (c , Name (String (str (c ))))
49+ if isinstance (c , int )
50+ else Double (str (c ))
51+ )
52+
4653 # used for optimization
4754 from neuron .nmodl .visitor import AstLookupVisitor
4855 from neuron .nmodl import dsl
@@ -330,6 +337,7 @@ def ast(self):
330337 from . import Rate , rxd , MultiCompartmentReaction
331338 from .species import (
332339 SpeciesOnRegion ,
340+ Parameter ,
333341 ParameterOnRegion ,
334342 SpeciesOnExtracellular ,
335343 ParameterOnExtracellular ,
@@ -369,13 +377,18 @@ def parse_kinetic_block(reactions, blocks):
369377 s = sptr ()
370378 sid = self ._species_ids [s ._id ]
371379 for r in s ._regions :
380+ # skip regions not local here
381+ if r ._id not in self ._region_ids :
382+ continue
372383 rid = self ._region_ids [r ._id ]
373384 flat_name = s .ast (r ).get_node_name ()
374385 self ._name_to_index [flat_name ] = ("species" , sid , rid )
375386 for sptr in self ._react_params :
376387 s = sptr ()
377388 sid = self ._params_ids [s ._id ]
378389 for r in s ._regions :
390+ if r ._id not in self ._region_ids :
391+ continue
379392 rid = self ._region_ids [r ._id ]
380393 flat_name = s .ast (r ).get_node_name ()
381394 self ._name_to_index [flat_name ] = ("params" , sid , rid )
@@ -414,32 +427,62 @@ def parse_kinetic_block(reactions, blocks):
414427 rates = []
415428 multicompartmentReactions = []
416429 states = []
417- mc_flux = []
430+ # maps for kinetic-blocks
418431 mc_kinetic_blocks = []
419432 local_consts = {}
433+ mc_mult_count = 0
420434 for rptr , rlst in self ._react_regions .items ():
421435 r = rptr ()
422436 rast , sp = r .ast (rlst )
423437 species += sp
424438 rast = rast if hasattr (rast , "__len__" ) else [rast ]
425439 mc_rates_ast = []
426440 if isinstance (r , MultiCompartmentReaction ):
427- for react in rast :
428- if isinstance (react , ReactionStatement ):
429- mc_kinetic_blocks .append (react )
430- else :
431- mc_rates_ast .append (react )
432- multicompartmentReactions += mc_rates_ast
433- flux = []
441+ # keep track for the fluxes and area/volume multipliers
442+ mults = []
443+ fluxes = []
434444 for i , sp in enumerate (r ._sources + r ._dests ):
435445 s = sp ()
436- if r ._membrane_flux and isinstance (s , SpeciesOnRegion ):
437- sid = self ._species_ids [s ._id ]
438- rid = self ._region_ids [s ._region ()._id ]
439- flux .append ((sid , rid , r ._cur_charges [i ]))
446+ if r ._membrane_flux :
447+ if isinstance (s , SpeciesOnRegion ) and not isinstance (
448+ s , ParameterOnRegion
449+ ):
450+ sid = self ._species_ids [s ._id ]
451+ rid = self ._region_ids [s ._region ()._id ]
452+ flux = (sid , rid , r ._cur_charges [i ])
453+
454+ # keep track of direction
455+ kin_coeff = (
456+ - r ._cur_charges [i ]
457+ if i < len (r ._sources )
458+ else r ._cur_charges [i ]
459+ )
460+ flux = (sid , rid , r ._cur_charges [i ], kin_coeff )
461+ else :
462+ flux = None
440463 else :
441- flux .append (None )
442- mc_flux += flux
464+ flux = None
465+ if not isinstance (
466+ s , (Parameter , ParameterOnRegion , ParameterOnExtracellular )
467+ ):
468+ mults .append (mc_mult_count )
469+ fluxes .append (flux )
470+ mc_mult_count += 1
471+ for react , mid , flux in zip (rast , mults , fluxes ):
472+ if isinstance (react , ReactionStatement ):
473+ # ReactionStatement and mult with both account for direction
474+ flx = [
475+ (f [0 ], f [1 ], f [3 ]) if f is not None else f for f in fluxes
476+ ]
477+ mc_kinetic_blocks .append ((react , mults , flx ))
478+ else :
479+ if flux is None :
480+ flx = None
481+ else :
482+ sid , rid , charge , _ = flux
483+ flx = (sid , rid , charge )
484+ mc_rates_ast .append ((react , mid , flx ))
485+ multicompartmentReactions += mc_rates_ast
443486 elif (
444487 isinstance (rptr (), Rate )
445488 or _ast_config ["kinetic_block" ] == "off"
@@ -460,67 +503,84 @@ def parse_kinetic_block(reactions, blocks):
460503 )
461504 if states != []:
462505 blocks .append (StateBlock (states ))
463-
464506 # convert KineticBlock to DerivativeBlock to compile
465507 if reactions != []:
466508 rast , lc = parse_kinetic_block (reactions , blocks )
467509 rates += rast
468510 local_consts |= lc
469- if mc_kinetic_blocks != []:
470- rast , lc = parse_kinetic_block (mc_kinetic_blocks , blocks )
471- multicompartmentReactions += rast
511+ mc_div_block_count = len (multicompartmentReactions )
512+ mc_kinetic_blocks_mults = []
513+ for react , mult , flux in mc_kinetic_blocks :
514+ rast , lc = parse_kinetic_block (
515+ [react ],
516+ blocks ,
517+ )
518+
519+ for react , mid , flx in zip (rast , mult , flux ):
520+ multicompartmentReactions .append ((react , mid , flx ))
472521 local_consts |= lc
522+ mc_kinetic_blocks_mults += mult
473523 # Merge rates for common species or states
474524 if rates != [] or multicompartmentReactions != []:
475525 lookup = AstLookupVisitor ()
476526 merged = {}
477527 constants = set ()
478- mult_id = 0
479- for rid , stmt in enumerate (rates + multicompartmentReactions ):
528+ mc_stmt = 0
529+
530+ def _add_flux (flx , frhs ):
531+ # accumulate a membrane-flux contribution under key `flx`
532+ if flx in merged :
533+ merged [flx ] = (
534+ Name (String (flx )),
535+ BinaryExpression (
536+ merged [flx ][1 ],
537+ BinaryOperator (BinaryOp .BOP_ADDITION ),
538+ frhs ,
539+ ),
540+ )
541+ else :
542+ merged [flx ] = (Name (String (flx )), frhs )
543+ constants .add (flx )
544+
545+ for rid , statement in enumerate (rates + multicompartmentReactions ):
546+ if rid < len (rates ):
547+ stmt = statement
548+ else :
549+ stmt , mult_id , flux = statement
550+
480551 diffeq = stmt .expression
481552 binexpr = diffeq .expression
482553 var_name = binexpr .lhs .get_node_name ()
483554 rhs = ParenExpression (binexpr .rhs )
484555 if rid >= len (rates ):
485556 # MCR have to be multiplied by mult[]
486- mult = Name (String (f"_mult_{ mult_id } " ))
487- if mc_flux [mult_id ] is not None :
488- sid , rid , charge = mc_flux [mult_id ]
489- flx = f"_flux_{ sid } _{ rid } _"
490- fast = Name (String (flx ))
491- cast = (
492- Integer (charge , Name (String (str (charge ))))
493- if isinstance (charge , int )
494- else Double (str (charge ))
495- )
557+ if mult_id in mc_kinetic_blocks_mults :
558+ # Kinetic-block MC reaction
559+ # sign is already in d/dt so use abs(_mult[id])
560+ mult = Name (String (f"_absmult_{ mult_id } " ))
561+ constants .add (f"_absmult_{ mult_id } " )
562+ else :
563+ # Derivative MC reaction
564+ mult = Name (String (f"_mult_{ mult_id } " ))
565+ constants .add (f"_mult_{ mult_id } " )
566+ if flux is not None :
567+ sid , frid , charge = flux
496568 if charge == 1 :
497569 frhs = rhs
498570 else :
499571 frhs = BinaryExpression (
500- cast ,
572+ _int_or_double ( charge ) ,
501573 BinaryOperator (BinaryOp .BOP_MULTIPLICATION ),
502574 rhs ,
503575 )
504- if flx in merged :
505- merged [flx ] = (
506- fast ,
507- BinaryExpression (
508- merged [flx ][1 ],
509- BinaryOperator (BinaryOp .BOP_ADDITION ),
510- frhs ,
511- ),
512- )
513- else :
514- merged [flx ] = (fast , frhs )
515- constants .add (flx )
516- # multiple by a constant to scale units
576+ _add_flux (f"_flux_{ sid } _{ frid } _" , frhs )
577+
578+ # multiply by a constant to scale units
517579 rhs = BinaryExpression (
518580 mult ,
519581 BinaryOperator (BinaryOp .BOP_MULTIPLICATION ),
520582 rhs ,
521583 )
522- constants .add (f"_mult_{ mult_id } " )
523- mult_id += 1
524584 if var_name in merged :
525585 merged [var_name ] = (
526586 binexpr .lhs ,
@@ -587,6 +647,7 @@ def parse_kinetic_block(reactions, blocks):
587647 "var_names" : list (merged .keys ()),
588648 "code" : code ,
589649 "tmp_vars" : tmp_vars ,
650+ "merged" : merged ,
590651 }
591652
592653 blocks .append (
0 commit comments