Skip to content

BOLD monitoring#

BOLD monitoring utilities are provided in the module ANNarchy.extensions.bold, which must be explicitly imported:

from ANNarchy import *
from ANNarchy.extensions.bold import BoldMonitor

ANNarchy.extensions.bold.BoldMonitor #

:copyright: Copyright 2013 - now, see AUTHORS. :license: GPLv2, see LICENSE for details.

BoldMonitor #

Bases: object

Monitors the BOLD signal for several populations using a computational model.

The BOLD monitor transforms one or two input population variables (such as the mean firing rate) into a recordable BOLD signal according to a computational model (for example a variation of the Balloon model).

Source code in ANNarchy/extensions/bold/BoldMonitor.py
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
class BoldMonitor(object):
    """
    Monitors the BOLD signal for several populations using a computational model.

    The BOLD monitor transforms one or two input population variables (such as the mean firing rate) into a recordable BOLD signal according to a computational model (for example a variation of the Balloon model).
    """
    def __init__(self,
        populations=None,
        bold_model=balloon_RN,
        mapping={'I_CBF': 'r'},
        scale_factor=None,
        normalize_input=None,
        recorded_variables=None,
        start=False,
        net_id=0,
        copied=False):
        """
        :param populations: list of recorded populations.

        :param bold_model: computational model for BOLD signal defined as a BoldModel class/object (see ANNarchy.extensions.bold.PredefinedModels for more predefined examples). Default is `balloon_RN`.

        :param mapping: mapping dictionary between the inputs of the BOLD model (`I_CBF` for single inputs, `I_CBF` and `I_CMRO2` for double inputs in the provided examples) and the variables of the input populations. By default, `{'I_CBF': 'r'}` maps the firing rate `r` of the input population(s) to the variable `I_CBF` of the BOLD model. 

        :param scale_factor: list of float values to allow a weighting of signals between populations. By default, the input signal is weighted by the ratio of the population size to all populations within the recorded region.

        :param normalize_input: list of integer values which represent a optional baseline per population. The input signals will require an additional normalization using a baseline value. A value different from 0 represents the time period for determing this baseline in milliseconds (biological time).

        :param recorded_variables: which variables of the BOLD model should be recorded? (by default, the output variable of the BOLD model is added, e.g. ["BOLD"] for the provided examples).
        """
        self.net_id = net_id

        # instantiate if necessary, please note
        # that population will make a deepcopy on this objects
        if inspect.isclass(bold_model):
            bold_model = bold_model()

        # for reporting
        bold_model._model_instantiated = True

        # The usage of [] as default arguments in the __init__ call lead to strange side effects.
        # We decided therefore to use None as default and create the lists locally.
        if populations is None:
            Global._error("Either a population or a list of populations must be provided to the BOLD monitor (populations=...)")
        if scale_factor is None:
            scale_factor = []
        if normalize_input is None:
            normalize_input = []
        if recorded_variables is None:
            recorded_variables = []

        # argument check
        if not(isinstance(populations, list)):
            populations = [populations]
        if not(isinstance(scale_factor, list)):
            scale_factor = [scale_factor]*len(populations)
        if not(isinstance(normalize_input, list)):
            normalize_input = [normalize_input]*len(populations)
        if isinstance(recorded_variables, str):
            recorded_variables = [recorded_variables]

        if len(scale_factor) > 0:
            if len(populations) != len(scale_factor):
                Global._error("BoldMonitor: Length of scale_factor must be equal to number of populations")

        if len(normalize_input) > 0:
            if len(populations) != len(normalize_input):
                Global._error("BoldMonitor: Length of normalize_input must be equal to number of populations")

        # Check mapping
        for target, input_var in mapping.items():
            if not target in bold_model._inputs:
                Global._error("BoldMonitor: the key " + target + " of mapping is not part of the BOLD model.")

        # Check recorded variables
        if len(recorded_variables) == 0:
            recorded_variables = bold_model._output
        else:
            # Add the output variables (and remove doublons)
            l1 = bold_model._output
            l2 = [recorded_variables] if isinstance(recorded_variables, str) else recorded_variables
            recorded_variables = list(set(l2+l1))
            recorded_variables.sort()

        if not copied:
            # Add the container to the object management
            Global._network[0]['extensions'].append(self)
            self.id = len(Global._network[self.net_id]['extensions'])

            # create the population
            self._bold_pop = Population(1, neuron=bold_model, name= bold_model.name )
            self._bold_pop.enabled = start

            # create the monitor
            self._monitor = Monitor(self._bold_pop, recorded_variables, start=start)

            # create the projection(s)
            self._acc_proj = []

            if len(scale_factor) == 0:
                pop_overall_size = 0
                for _, pop in enumerate(populations):
                    pop_overall_size += pop.size

                # the conductance is normalized between [0 .. 1]. This scale factor
                # should balance different population sizes
                for _, pop in enumerate(populations):
                    scale_factor_conductance = float(pop.size)/float(pop_overall_size)
                    scale_factor.append(scale_factor_conductance)

            if len(normalize_input) == 0:
                normalize_input = [0] * len(populations)
                # TODO: can we check if users used NormProjections? If not, this will crash ...

            for target, input_var in mapping.items():
                for pop, scale, normalize in zip(populations, scale_factor, normalize_input):

                    tmp_proj = AccProjection(pre = pop, post=self._bold_pop, target=target, variable=input_var, scale_factor=scale, normalize_input=normalize)

                    tmp_proj.connect_all_to_all(weights= 1.0)

                    self._acc_proj.append(tmp_proj)

        else:
            # Add the container to the object management
            Global._network[net_id]['extensions'].append(self)
            self.id = len(Global._network[self.net_id]['extensions'])

            # instances are assigned by the copying instance
            self._bold_pop = None
            self._monitor = None
            self._acc_proj = []

        self.name = "bold_monitor"

        # store arguments for copy
        self._populations = populations
        self._bold_model = bold_model
        self._mapping = mapping
        self._scale_factor = scale_factor
        self._normalize_input = normalize_input
        self._recorded_variables = recorded_variables
        self._start = start

        # Finalize initialization
        self._initialized = True if not copied else False

    #
    #   MONITOR functions
    #
    def start(self):
        """
        Same as `ANNarchy.core.Monitor.start()`
        """
        self._monitor.start()

        # enable ODEs
        self._bold_pop.cyInstance.activate(True)

        # check if we have projections with baseline
        for proj in self._acc_proj:
            if proj._normalize_input > 0:
                proj.cyInstance.start(proj._normalize_input/Global.config["dt"])

    def stop(self):
        """
        Same as `ANNarchy.core.Monitor.stop()`
        """
        self._monitor.stop()

        # enable ODEs
        self._bold_pop.cyInstance.activate(False)

    def get(self, variable):
        """
        Same as `ANNarchy.core.Monitor.get()`
        """
        return self._monitor.get(variable)


    #
    #   POPULATION functions i. e. access to model parameter
    #

    # Method called when accessing an attribute.
    # We overload the default to allow access to monitor variables.
    def __getattr__(self, name):

        if name == '_initialized' or not hasattr(self, '_initialized'): # Before the end of the constructor
            return object.__getattribute__(self, name)

        if self._initialized:
            if self._bold_pop.initialized == False:
                Global._error("BoldMonitor: attributes can not modified before compile()")

            if name in self._bold_pop.attributes:
                return getattr(self._bold_pop, name)

        return object.__getattribute__(self, name)

    # Method called when accessing an attribute.
    # We overload the default to allow access to monitor variables.
    def __setattr__(self, name, value):

        if name == '_initialized' or not hasattr(self, '_initialized'): # Before the end of the constructor
            return object.__setattr__(self, name, value)

        if self._initialized:
            if self._bold_pop.initialized == False:
                Global._error("BoldMonitor: attributes can not modified before compile()")

            if name in self._bold_pop.attributes:
                setattr(self._bold_pop, name, value)
            else:
                raise AttributeError("the variable '"+str(name)+ "' is not an attribute of the bold model.")

        else:
            object.__setattr__(self, name, value)

    #
    # Destruction
    def _clear(self):
        pass

__init__(populations=None, bold_model=balloon_RN, mapping={'I_CBF': 'r'}, scale_factor=None, normalize_input=None, recorded_variables=None, start=False, net_id=0, copied=False) #

Parameters:

  • populations

    list of recorded populations.

  • bold_model

    computational model for BOLD signal defined as a BoldModel class/object (see ANNarchy.extensions.bold.PredefinedModels for more predefined examples). Default is balloon_RN.

  • mapping

    mapping dictionary between the inputs of the BOLD model (I_CBF for single inputs, I_CBF and I_CMRO2 for double inputs in the provided examples) and the variables of the input populations. By default, {'I_CBF': 'r'} maps the firing rate r of the input population(s) to the variable I_CBF of the BOLD model.

  • scale_factor

    list of float values to allow a weighting of signals between populations. By default, the input signal is weighted by the ratio of the population size to all populations within the recorded region.

  • normalize_input

    list of integer values which represent a optional baseline per population. The input signals will require an additional normalization using a baseline value. A value different from 0 represents the time period for determing this baseline in milliseconds (biological time).

  • recorded_variables

    which variables of the BOLD model should be recorded? (by default, the output variable of the BOLD model is added, e.g. ["BOLD"] for the provided examples).

Source code in ANNarchy/extensions/bold/BoldMonitor.py
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
def __init__(self,
    populations=None,
    bold_model=balloon_RN,
    mapping={'I_CBF': 'r'},
    scale_factor=None,
    normalize_input=None,
    recorded_variables=None,
    start=False,
    net_id=0,
    copied=False):
    """
    :param populations: list of recorded populations.

    :param bold_model: computational model for BOLD signal defined as a BoldModel class/object (see ANNarchy.extensions.bold.PredefinedModels for more predefined examples). Default is `balloon_RN`.

    :param mapping: mapping dictionary between the inputs of the BOLD model (`I_CBF` for single inputs, `I_CBF` and `I_CMRO2` for double inputs in the provided examples) and the variables of the input populations. By default, `{'I_CBF': 'r'}` maps the firing rate `r` of the input population(s) to the variable `I_CBF` of the BOLD model. 

    :param scale_factor: list of float values to allow a weighting of signals between populations. By default, the input signal is weighted by the ratio of the population size to all populations within the recorded region.

    :param normalize_input: list of integer values which represent a optional baseline per population. The input signals will require an additional normalization using a baseline value. A value different from 0 represents the time period for determing this baseline in milliseconds (biological time).

    :param recorded_variables: which variables of the BOLD model should be recorded? (by default, the output variable of the BOLD model is added, e.g. ["BOLD"] for the provided examples).
    """
    self.net_id = net_id

    # instantiate if necessary, please note
    # that population will make a deepcopy on this objects
    if inspect.isclass(bold_model):
        bold_model = bold_model()

    # for reporting
    bold_model._model_instantiated = True

    # The usage of [] as default arguments in the __init__ call lead to strange side effects.
    # We decided therefore to use None as default and create the lists locally.
    if populations is None:
        Global._error("Either a population or a list of populations must be provided to the BOLD monitor (populations=...)")
    if scale_factor is None:
        scale_factor = []
    if normalize_input is None:
        normalize_input = []
    if recorded_variables is None:
        recorded_variables = []

    # argument check
    if not(isinstance(populations, list)):
        populations = [populations]
    if not(isinstance(scale_factor, list)):
        scale_factor = [scale_factor]*len(populations)
    if not(isinstance(normalize_input, list)):
        normalize_input = [normalize_input]*len(populations)
    if isinstance(recorded_variables, str):
        recorded_variables = [recorded_variables]

    if len(scale_factor) > 0:
        if len(populations) != len(scale_factor):
            Global._error("BoldMonitor: Length of scale_factor must be equal to number of populations")

    if len(normalize_input) > 0:
        if len(populations) != len(normalize_input):
            Global._error("BoldMonitor: Length of normalize_input must be equal to number of populations")

    # Check mapping
    for target, input_var in mapping.items():
        if not target in bold_model._inputs:
            Global._error("BoldMonitor: the key " + target + " of mapping is not part of the BOLD model.")

    # Check recorded variables
    if len(recorded_variables) == 0:
        recorded_variables = bold_model._output
    else:
        # Add the output variables (and remove doublons)
        l1 = bold_model._output
        l2 = [recorded_variables] if isinstance(recorded_variables, str) else recorded_variables
        recorded_variables = list(set(l2+l1))
        recorded_variables.sort()

    if not copied:
        # Add the container to the object management
        Global._network[0]['extensions'].append(self)
        self.id = len(Global._network[self.net_id]['extensions'])

        # create the population
        self._bold_pop = Population(1, neuron=bold_model, name= bold_model.name )
        self._bold_pop.enabled = start

        # create the monitor
        self._monitor = Monitor(self._bold_pop, recorded_variables, start=start)

        # create the projection(s)
        self._acc_proj = []

        if len(scale_factor) == 0:
            pop_overall_size = 0
            for _, pop in enumerate(populations):
                pop_overall_size += pop.size

            # the conductance is normalized between [0 .. 1]. This scale factor
            # should balance different population sizes
            for _, pop in enumerate(populations):
                scale_factor_conductance = float(pop.size)/float(pop_overall_size)
                scale_factor.append(scale_factor_conductance)

        if len(normalize_input) == 0:
            normalize_input = [0] * len(populations)
            # TODO: can we check if users used NormProjections? If not, this will crash ...

        for target, input_var in mapping.items():
            for pop, scale, normalize in zip(populations, scale_factor, normalize_input):

                tmp_proj = AccProjection(pre = pop, post=self._bold_pop, target=target, variable=input_var, scale_factor=scale, normalize_input=normalize)

                tmp_proj.connect_all_to_all(weights= 1.0)

                self._acc_proj.append(tmp_proj)

    else:
        # Add the container to the object management
        Global._network[net_id]['extensions'].append(self)
        self.id = len(Global._network[self.net_id]['extensions'])

        # instances are assigned by the copying instance
        self._bold_pop = None
        self._monitor = None
        self._acc_proj = []

    self.name = "bold_monitor"

    # store arguments for copy
    self._populations = populations
    self._bold_model = bold_model
    self._mapping = mapping
    self._scale_factor = scale_factor
    self._normalize_input = normalize_input
    self._recorded_variables = recorded_variables
    self._start = start

    # Finalize initialization
    self._initialized = True if not copied else False

get(variable) #

Same as ANNarchy.core.Monitor.get()

Source code in ANNarchy/extensions/bold/BoldMonitor.py
187
188
189
190
191
def get(self, variable):
    """
    Same as `ANNarchy.core.Monitor.get()`
    """
    return self._monitor.get(variable)

start() #

Same as ANNarchy.core.Monitor.start()

Source code in ANNarchy/extensions/bold/BoldMonitor.py
164
165
166
167
168
169
170
171
172
173
174
175
176
def start(self):
    """
    Same as `ANNarchy.core.Monitor.start()`
    """
    self._monitor.start()

    # enable ODEs
    self._bold_pop.cyInstance.activate(True)

    # check if we have projections with baseline
    for proj in self._acc_proj:
        if proj._normalize_input > 0:
            proj.cyInstance.start(proj._normalize_input/Global.config["dt"])

stop() #

Same as ANNarchy.core.Monitor.stop()

Source code in ANNarchy/extensions/bold/BoldMonitor.py
178
179
180
181
182
183
184
185
def stop(self):
    """
    Same as `ANNarchy.core.Monitor.stop()`
    """
    self._monitor.stop()

    # enable ODEs
    self._bold_pop.cyInstance.activate(False)

BOLD models#

The provided BOLD models follow the Balloon model (Buxton et al., 1998) with the different variations studied in (Stephan et al., 2007). Those models all compute the vascular response to neural activity through a dampened oscillator:

\[ \frac{ds}{dt} = \phi \, I_\text{CBF} - \kappa \, s - \gamma \, (f_{in} - 1) \]
\[ \frac{df_{in}}{dt} = s \]

This allows to compute the oxygen extraction fraction:

\[ E = 1 - (1 - E_{0})^{ \frac{1}{f_{in}} } \]

The (normalized) venous blood volume is computed as:

\[ \tau_0 \, \frac{dv}{dt} = (f_{in} - f_{out}) \]
\[ f_{out} = v^{\frac{1}{\alpha}} \]

The level of deoxyhemoglobin into the venous compartment is computed by:

\[ \tau_0 \, \frac{dq}{dt} = f_{in} \, \frac{E}{E_0} - \frac{q}{v} \, f_{out} \]

Using the two signals \(v\) and \(q\), there are two ways to compute the corresponding BOLD signal:

  • N: Non-linear BOLD equation:
\[ BOLD = v_0 \, ( k_1 \, (1-q) + k_2 \, (1- \dfrac{q}{v}) + k_3 \, (1 - v) ) \]
  • L: Linear BOLD equation:
\[ BOLD = v_0 \, ((k_1 + k_2) \, (1 - q) + (k_3 - k_2) \, (1 - v)) \]

Additionally, the three coefficients \(k_1\), \(k_2\), \(k_3\) can be computed in two different ways:

  • C: classical coefficients from (Buxton et al., 1998):
\[k_1 = (1 - v_0) \, 4.3 \, v_0 \, E_0 \, \text{TE}\]
\[k_2 = 2 \, E_0\]
\[k_3 = 1 - \epsilon\]
  • R: revised coefficients from (Obata et al., 2004):
\[k_1 = 4.3 \, v_0 \, E_0 \, \text{TE}\]
\[k_2 = \epsilon \, r_0 \, E_0 \, \text{TE}\]
\[k_3 = 1 - \epsilon\]

This makes a total of four different BOLD model (RN, RL, CN, CL) which are provided by the extension. The different parameters can be modified in the constructor. Additionally, we also provide the model that was used in (Maith et al., 2021) and the two-inputs model of (Maith et al, 2022).

ANNarchy.extensions.bold.BoldModel #

:copyright: Copyright 2013 - now, see AUTHORS. :license: GPLv2, see LICENSE for details.

BoldModel #

Bases: Neuron

Base class to define a BOLD model to be used in a BOLD monitor.

A BOLD model is quite similar to a regular rate-coded neuron. It gets a weighted sum of inputs with a specific target (e.g. I_CBF) and compute a single output variable (called BOLD in the predefined models, but it could be anything).

The main difference is that a BOLD model should also declare which targets are used for the input signal:

bold_model = BoldModel(
    parameters = '''
        tau = 1000.
    ''',
    equations = '''
        I_CBF = sum(I_CBF)
        # ...
        tau * dBOLD/dt = I_CBF - BOLD
    ''',
    inputs = "I_CBF"
)
Source code in ANNarchy/extensions/bold/BoldModel.py
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class BoldModel(Neuron):
    """
    Base class to define a BOLD model to be used in a BOLD monitor.

    A BOLD model is quite similar to a regular rate-coded neuron. It gets a weighted sum of inputs with a specific target (e.g. I_CBF) and compute a single output variable (called `BOLD` in the predefined models, but it could be anything).

    The main difference is that a BOLD model should also declare which targets are used for the input signal:

    ```python
    bold_model = BoldModel(
        parameters = '''
            tau = 1000.
        ''',
        equations = '''
            I_CBF = sum(I_CBF)
            # ...
            tau * dBOLD/dt = I_CBF - BOLD
        ''',
        inputs = "I_CBF"
    )
    ```
    """
    def __init__(self, parameters, equations, inputs, output=["BOLD"], name="Custom BOLD model", description=""):
        """
        See ANNarchy.extensions.bold.PredefinedModels.py for some example models.

        :param parameters: parameters of the model and their initial value.
        :param equations: equations defining the temporal evolution of variables.
        :param inputs: single variable or list of input signals (e.g. 'I_CBF' or ['I_CBF', 'I_CMRO2']).
        :param output: output variable of the model (default is 'BOLD').
        :param name: optional model name.
        :param description: optional model description.
        """
        # The processing in BoldMonitor expects lists, but the interface
        # should allow also single strings (if only one variable is considered)
        self._inputs = [inputs] if isinstance(inputs, str) else inputs
        self._output = [output] if isinstance(output, str) else output

        Neuron.__init__(self, parameters=parameters, equations=equations, name=name, description=description)

        self._model_instantiated = False    # activated by BoldMonitor
__init__(parameters, equations, inputs, output=['BOLD'], name='Custom BOLD model', description='') #

See ANNarchy.extensions.bold.PredefinedModels.py for some example models.

Parameters:

  • parameters

    parameters of the model and their initial value.

  • equations

    equations defining the temporal evolution of variables.

  • inputs

    single variable or list of input signals (e.g. 'I_CBF' or ['I_CBF', 'I_CMRO2']).

  • output

    output variable of the model (default is 'BOLD').

  • name

    optional model name.

  • description

    optional model description.

Source code in ANNarchy/extensions/bold/BoldModel.py
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
def __init__(self, parameters, equations, inputs, output=["BOLD"], name="Custom BOLD model", description=""):
    """
    See ANNarchy.extensions.bold.PredefinedModels.py for some example models.

    :param parameters: parameters of the model and their initial value.
    :param equations: equations defining the temporal evolution of variables.
    :param inputs: single variable or list of input signals (e.g. 'I_CBF' or ['I_CBF', 'I_CMRO2']).
    :param output: output variable of the model (default is 'BOLD').
    :param name: optional model name.
    :param description: optional model description.
    """
    # The processing in BoldMonitor expects lists, but the interface
    # should allow also single strings (if only one variable is considered)
    self._inputs = [inputs] if isinstance(inputs, str) else inputs
    self._output = [output] if isinstance(output, str) else output

    Neuron.__init__(self, parameters=parameters, equations=equations, name=name, description=description)

    self._model_instantiated = False    # activated by BoldMonitor

ANNarchy.extensions.bold.balloon_RN #

Bases: BoldModel

A balloon model with revised coefficients and non-linear BOLD equation derived from Stephan et al. (2007).

Equivalent code:

balloon_RN = BoldModel(
    parameters = '''
        second    = 1000.0
        phi       = 1.0
        kappa     = 1/1.54
        gamma     = 1/2.46
        E_0       = 0.34
        tau       = 0.98
        alpha     = 0.33
        V_0       = 0.02
        v_0       = 40.3
        TE        = 40/1000.
        epsilon   = 1.43
        r_0       = 25.
    ''',
    equations = '''
        # Single input
        I_CBF          = sum(I_CBF)                                                : init=0
        ds/dt          = (phi * I_CBF - kappa * s - gamma * (f_in - 1))/second     : init=0
        df_in/dt       = s / second                                                : init=1, min=0.01

        E              = 1 - (1 - E_0)**(1 / f_in)                                 : init=0.3424
        dq/dt          = (f_in * E / E_0 - (q / v) * f_out)/(tau*second)           : init=1, min=0.01
        dv/dt          = (f_in - f_out)/(tau*second)                               : init=1, min=0.01
        f_out          = v**(1 / alpha)                                            : init=1, min=0.01

        # Revised coefficients
        k_1            = 4.3 * v_0 * E_0 * TE
        k_2            = epsilon * r_0 * E_0 * TE
        k_3            = 1.0 - epsilon

        # Non-linear equation
        BOLD           = V_0 * (k_1 * (1 - q) + k_2 * (1 - (q / v)) + k_3 * (1 - v))  : init=0
    ''',
    inputs="I_CBF",
)
Source code in ANNarchy/extensions/bold/PredefinedModels.py
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
class balloon_RN(BoldModel):
    """
    A balloon model with revised coefficients and non-linear BOLD equation derived from Stephan et al. (2007).

    Equivalent code:

    ```python
    balloon_RN = BoldModel(
        parameters = '''
            second    = 1000.0
            phi       = 1.0
            kappa     = 1/1.54
            gamma     = 1/2.46
            E_0       = 0.34
            tau       = 0.98
            alpha     = 0.33
            V_0       = 0.02
            v_0       = 40.3
            TE        = 40/1000.
            epsilon   = 1.43
            r_0       = 25.
        ''',
        equations = '''
            # Single input
            I_CBF          = sum(I_CBF)                                                : init=0
            ds/dt          = (phi * I_CBF - kappa * s - gamma * (f_in - 1))/second     : init=0
            df_in/dt       = s / second                                                : init=1, min=0.01

            E              = 1 - (1 - E_0)**(1 / f_in)                                 : init=0.3424
            dq/dt          = (f_in * E / E_0 - (q / v) * f_out)/(tau*second)           : init=1, min=0.01
            dv/dt          = (f_in - f_out)/(tau*second)                               : init=1, min=0.01
            f_out          = v**(1 / alpha)                                            : init=1, min=0.01

            # Revised coefficients
            k_1            = 4.3 * v_0 * E_0 * TE
            k_2            = epsilon * r_0 * E_0 * TE
            k_3            = 1.0 - epsilon

            # Non-linear equation
            BOLD           = V_0 * (k_1 * (1 - q) + k_2 * (1 - (q / v)) + k_3 * (1 - v))  : init=0
        ''',
        inputs="I_CBF",
    )
    ```
    """
    def __init__(self,
            phi       = 1.0,
            kappa     = 1/1.54,
            gamma     = 1/2.46,
            E_0       = 0.34,
            tau       = 0.98,
            alpha     = 0.33,
            V_0       = 0.02,
            v_0       = 40.3,
            TE        = 40/1000.,
            epsilon   = 1.43,
            r_0       = 25,
        ):

        """
        :param phi:       input coefficient
        :param kappa:     signal decay
        :param gamma:     feedback regulation
        :param E_0:       oxygen extraction fraction at rest
        :param tau:       time constant (in s!)
        :param alpha:     vessel stiffness
        :param V_0:       resting venous blood volume fraction
        :param v_0:       frequency offset at the outer surface of the magnetized vessel for fully deoxygenated blood at 1.5 T
        :param TE:        echo time
        :param epsilon:   ratio of intra- and extravascular signal
        :param r_0:       slope of the relation between the intravascular relaxation rate and oxygen saturation
        """
        parameters = """
            second    = 1000.0 : population
            phi       = %(phi)s : population
            kappa     = %(kappa)s : population
            gamma     = %(gamma)s : population
            E_0       = %(E_0)s : population
            tau       = %(tau)s : population
            alpha     = %(alpha)s : population
            V_0       = %(V_0)s : population
            v_0       = %(v_0)s : population
            TE        = %(TE)s : population
            epsilon   = %(epsilon)s : population
            r_0       = %(r_0)s : population
        """ % {
            'phi': phi,
            'kappa': kappa,
            'gamma': gamma,
            'E_0': E_0,
            'tau': tau,
            'alpha': alpha,
            'V_0': V_0,
            'v_0': v_0,
            'TE': TE,
            'epsilon': epsilon,
            'r_0': r_0,
        }

        equations = """
            # Single input
            I_CBF          = sum(I_CBF)                                                : init=0
            ds/dt          = (phi * I_CBF - kappa * s - gamma * (f_in - 1))/second     : init=0
            df_in/dt       = s / second                                                : init=1, min=0.01

            E              = 1 - (1 - E_0)**(1 / f_in)                                 : init=0.3424
            dq/dt          = (f_in * E / E_0 - (q / v) * f_out)/(tau*second)           : init=1, min=0.01
            dv/dt          = (f_in - f_out)/(tau*second)                               : init=1, min=0.01
            f_out          = v**(1 / alpha)                                            : init=1, min=0.01

            # Revised coefficients
            k_1            = 4.3 * v_0 * E_0 * TE
            k_2            = epsilon * r_0 * E_0 * TE
            k_3            = 1.0 - epsilon

            # Non-linear equation
            BOLD           = V_0 * (k_1 * (1 - q) + k_2 * (1 - (q / v)) + k_3 * (1 - v))  : init=0
        """
        name = "BOLD model RN"
        description = "BOLD computation with revised coefficients and non-linear BOLD equation (Stephan et al., 2007)."

        BoldModel.__init__(self, 
            parameters=parameters, 
            equations=equations,  
            inputs="I_CBF",
            output="BOLD",
            name=name, description=description
        )

__init__(phi=1.0, kappa=1 / 1.54, gamma=1 / 2.46, E_0=0.34, tau=0.98, alpha=0.33, V_0=0.02, v_0=40.3, TE=40 / 1000.0, epsilon=1.43, r_0=25) #

Parameters:

  • phi

    input coefficient

  • kappa

    signal decay

  • gamma

    feedback regulation

  • E_0

    oxygen extraction fraction at rest

  • tau

    time constant (in s!)

  • alpha

    vessel stiffness

  • V_0

    resting venous blood volume fraction

  • v_0

    frequency offset at the outer surface of the magnetized vessel for fully deoxygenated blood at 1.5 T

  • TE

    echo time

  • epsilon

    ratio of intra- and extravascular signal

  • r_0

    slope of the relation between the intravascular relaxation rate and oxygen saturation

Source code in ANNarchy/extensions/bold/PredefinedModels.py
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
def __init__(self,
        phi       = 1.0,
        kappa     = 1/1.54,
        gamma     = 1/2.46,
        E_0       = 0.34,
        tau       = 0.98,
        alpha     = 0.33,
        V_0       = 0.02,
        v_0       = 40.3,
        TE        = 40/1000.,
        epsilon   = 1.43,
        r_0       = 25,
    ):

    """
    :param phi:       input coefficient
    :param kappa:     signal decay
    :param gamma:     feedback regulation
    :param E_0:       oxygen extraction fraction at rest
    :param tau:       time constant (in s!)
    :param alpha:     vessel stiffness
    :param V_0:       resting venous blood volume fraction
    :param v_0:       frequency offset at the outer surface of the magnetized vessel for fully deoxygenated blood at 1.5 T
    :param TE:        echo time
    :param epsilon:   ratio of intra- and extravascular signal
    :param r_0:       slope of the relation between the intravascular relaxation rate and oxygen saturation
    """
    parameters = """
        second    = 1000.0 : population
        phi       = %(phi)s : population
        kappa     = %(kappa)s : population
        gamma     = %(gamma)s : population
        E_0       = %(E_0)s : population
        tau       = %(tau)s : population
        alpha     = %(alpha)s : population
        V_0       = %(V_0)s : population
        v_0       = %(v_0)s : population
        TE        = %(TE)s : population
        epsilon   = %(epsilon)s : population
        r_0       = %(r_0)s : population
    """ % {
        'phi': phi,
        'kappa': kappa,
        'gamma': gamma,
        'E_0': E_0,
        'tau': tau,
        'alpha': alpha,
        'V_0': V_0,
        'v_0': v_0,
        'TE': TE,
        'epsilon': epsilon,
        'r_0': r_0,
    }

    equations = """
        # Single input
        I_CBF          = sum(I_CBF)                                                : init=0
        ds/dt          = (phi * I_CBF - kappa * s - gamma * (f_in - 1))/second     : init=0
        df_in/dt       = s / second                                                : init=1, min=0.01

        E              = 1 - (1 - E_0)**(1 / f_in)                                 : init=0.3424
        dq/dt          = (f_in * E / E_0 - (q / v) * f_out)/(tau*second)           : init=1, min=0.01
        dv/dt          = (f_in - f_out)/(tau*second)                               : init=1, min=0.01
        f_out          = v**(1 / alpha)                                            : init=1, min=0.01

        # Revised coefficients
        k_1            = 4.3 * v_0 * E_0 * TE
        k_2            = epsilon * r_0 * E_0 * TE
        k_3            = 1.0 - epsilon

        # Non-linear equation
        BOLD           = V_0 * (k_1 * (1 - q) + k_2 * (1 - (q / v)) + k_3 * (1 - v))  : init=0
    """
    name = "BOLD model RN"
    description = "BOLD computation with revised coefficients and non-linear BOLD equation (Stephan et al., 2007)."

    BoldModel.__init__(self, 
        parameters=parameters, 
        equations=equations,  
        inputs="I_CBF",
        output="BOLD",
        name=name, description=description
    )

ANNarchy.extensions.bold.balloon_RL #

Bases: BoldModel

A balloon model with revised coefficients and linear BOLD equation derived from Stephan et al. (2007).

Equivalent code:

balloon_RL = BoldModel(
    parameters = '''
        second    = 1000.0
        phi       = 1.0
        kappa     = 1/1.54
        gamma     = 1/2.46
        E_0       = 0.34
        tau       = 0.98
        alpha     = 0.33
        V_0       = 0.02
        v_0       = 40.3
        TE        = 40/1000.
        epsilon   = 1.43
        r_0       = 25.
    ''',
    equations = '''
        # Single input
        I_CBF          = sum(I_CBF)                                                : init=0
        ds/dt          = (phi * I_CBF - kappa * s - gamma * (f_in - 1))/second     : init=0
        df_in/dt       = s / second                                                : init=1, min=0.01

        E              = 1 - (1 - E_0)**(1 / f_in)                                 : init=0.3424
        dq/dt          = (f_in * E / E_0 - (q / v) * f_out)/(tau*second)           : init=1, min=0.01
        dv/dt          = (f_in - f_out)/(tau*second)                               : init=1, min=0.01
        f_out          = v**(1 / alpha)                                            : init=1, min=0.01

        # Revised coefficients
        k_1            = 4.3 * v_0 * E_0 * TE
        k_2            = epsilon * r_0 * E_0 * TE
        k_3            = 1.0 - epsilon

        # Linear equation
        BOLD           = V_0 * ((k_1 + k_2) * (1 - q) + (k_3 - k_2) * (1 - v))         : init=0
    ''',
    inputs="I_CBF",
)
Source code in ANNarchy/extensions/bold/PredefinedModels.py
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
class balloon_RL(BoldModel):
    """
    A balloon model with revised coefficients and linear BOLD equation derived from Stephan et al. (2007).

    Equivalent code:

    ```python
    balloon_RL = BoldModel(
        parameters = '''
            second    = 1000.0
            phi       = 1.0
            kappa     = 1/1.54
            gamma     = 1/2.46
            E_0       = 0.34
            tau       = 0.98
            alpha     = 0.33
            V_0       = 0.02
            v_0       = 40.3
            TE        = 40/1000.
            epsilon   = 1.43
            r_0       = 25.
        ''',
        equations = '''
            # Single input
            I_CBF          = sum(I_CBF)                                                : init=0
            ds/dt          = (phi * I_CBF - kappa * s - gamma * (f_in - 1))/second     : init=0
            df_in/dt       = s / second                                                : init=1, min=0.01

            E              = 1 - (1 - E_0)**(1 / f_in)                                 : init=0.3424
            dq/dt          = (f_in * E / E_0 - (q / v) * f_out)/(tau*second)           : init=1, min=0.01
            dv/dt          = (f_in - f_out)/(tau*second)                               : init=1, min=0.01
            f_out          = v**(1 / alpha)                                            : init=1, min=0.01

            # Revised coefficients
            k_1            = 4.3 * v_0 * E_0 * TE
            k_2            = epsilon * r_0 * E_0 * TE
            k_3            = 1.0 - epsilon

            # Linear equation
            BOLD           = V_0 * ((k_1 + k_2) * (1 - q) + (k_3 - k_2) * (1 - v))         : init=0
        ''',
        inputs="I_CBF",
    )
    ```
    """
    def __init__(self,
            phi       = 1.0,
            kappa     = 1/1.54,
            gamma     = 1/2.46,
            E_0       = 0.34,
            tau       = 0.98,
            alpha     = 0.33,
            V_0       = 0.02,
            v_0       = 40.3,
            TE        = 40/1000.,
            epsilon   = 1.43,
            r_0       = 25,
        ):

        """
        :param phi:       input coefficient
        :param kappa:     signal decay
        :param gamma:     feedback regulation
        :param E_0:       oxygen extraction fraction at rest
        :param tau:       time constant (in s!)
        :param alpha:     vessel stiffness
        :param V_0:       resting venous blood volume fraction
        :param v_0:       frequency offset at the outer surface of the magnetized vessel for fully deoxygenated blood at 1.5 T
        :param TE:        echo time
        :param epsilon:   ratio of intra- and extravascular signal
        :param r_0:       slope of the relation between the intravascular relaxation rate and oxygen saturation
        """

        parameters = """
            second    = 1000.0 : population
            phi       = %(phi)s : population
            kappa     = %(kappa)s : population
            gamma     = %(gamma)s : population
            E_0       = %(E_0)s : population
            tau       = %(tau)s : population
            alpha     = %(alpha)s : population
            V_0       = %(V_0)s : population
            v_0       = %(v_0)s : population
            TE        = %(TE)s : population
            epsilon   = %(epsilon)s : population
            r_0       = %(r_0)s : population
        """ % {
            'phi': phi,
            'kappa': kappa,
            'gamma': gamma,
            'E_0': E_0,
            'tau': tau,
            'alpha': alpha,
            'V_0': V_0,
            'v_0': v_0,
            'TE': TE,
            'epsilon': epsilon,
            'r_0': r_0,
        }

        equations = """
            # Single input
            I_CBF          = sum(I_CBF)                                                    : init=0
            ds/dt          = (phi * I_CBF - kappa * s - gamma * (f_in - 1))/second         : init=0
            df_in/dt       = s  / second                                                   : init=1, min=0.01

            E              = 1 - (1 - E_0)**(1 / f_in)                                     : init=0.3424
            tau*dq/dt      = f_in * E / E_0 - (q / v) * f_out                              : init=1, min=0.01
            tau*dv/dt      = f_in - f_out                                                  : init=1, min=0.01
            f_out          = v**(1 / alpha)                                                : init=1, min=0.01

            # Revised coeeficients
            k_1            = 4.3 * v_0 * E_0 * TE
            k_2            = epsilon * r_0 * E_0 * TE
            k_3            = 1 - epsilon

            # Linear equation
            BOLD           = V_0 * ((k_1 + k_2) * (1 - q) + (k_3 - k_2) * (1 - v))         : init=0
        """
        name = "BOLD model RL"
        description = "BOLD computation with revised coefficients and linear BOLD equation (Stephan et al., 2007)."

        BoldModel.__init__(self, 
            parameters=parameters, 
            equations=equations,  
            inputs="I_CBF",
            output="BOLD",
            name=name, description=description
        )

__init__(phi=1.0, kappa=1 / 1.54, gamma=1 / 2.46, E_0=0.34, tau=0.98, alpha=0.33, V_0=0.02, v_0=40.3, TE=40 / 1000.0, epsilon=1.43, r_0=25) #

Parameters:

  • phi

    input coefficient

  • kappa

    signal decay

  • gamma

    feedback regulation

  • E_0

    oxygen extraction fraction at rest

  • tau

    time constant (in s!)

  • alpha

    vessel stiffness

  • V_0

    resting venous blood volume fraction

  • v_0

    frequency offset at the outer surface of the magnetized vessel for fully deoxygenated blood at 1.5 T

  • TE

    echo time

  • epsilon

    ratio of intra- and extravascular signal

  • r_0

    slope of the relation between the intravascular relaxation rate and oxygen saturation

Source code in ANNarchy/extensions/bold/PredefinedModels.py
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
def __init__(self,
        phi       = 1.0,
        kappa     = 1/1.54,
        gamma     = 1/2.46,
        E_0       = 0.34,
        tau       = 0.98,
        alpha     = 0.33,
        V_0       = 0.02,
        v_0       = 40.3,
        TE        = 40/1000.,
        epsilon   = 1.43,
        r_0       = 25,
    ):

    """
    :param phi:       input coefficient
    :param kappa:     signal decay
    :param gamma:     feedback regulation
    :param E_0:       oxygen extraction fraction at rest
    :param tau:       time constant (in s!)
    :param alpha:     vessel stiffness
    :param V_0:       resting venous blood volume fraction
    :param v_0:       frequency offset at the outer surface of the magnetized vessel for fully deoxygenated blood at 1.5 T
    :param TE:        echo time
    :param epsilon:   ratio of intra- and extravascular signal
    :param r_0:       slope of the relation between the intravascular relaxation rate and oxygen saturation
    """

    parameters = """
        second    = 1000.0 : population
        phi       = %(phi)s : population
        kappa     = %(kappa)s : population
        gamma     = %(gamma)s : population
        E_0       = %(E_0)s : population
        tau       = %(tau)s : population
        alpha     = %(alpha)s : population
        V_0       = %(V_0)s : population
        v_0       = %(v_0)s : population
        TE        = %(TE)s : population
        epsilon   = %(epsilon)s : population
        r_0       = %(r_0)s : population
    """ % {
        'phi': phi,
        'kappa': kappa,
        'gamma': gamma,
        'E_0': E_0,
        'tau': tau,
        'alpha': alpha,
        'V_0': V_0,
        'v_0': v_0,
        'TE': TE,
        'epsilon': epsilon,
        'r_0': r_0,
    }

    equations = """
        # Single input
        I_CBF          = sum(I_CBF)                                                    : init=0
        ds/dt          = (phi * I_CBF - kappa * s - gamma * (f_in - 1))/second         : init=0
        df_in/dt       = s  / second                                                   : init=1, min=0.01

        E              = 1 - (1 - E_0)**(1 / f_in)                                     : init=0.3424
        tau*dq/dt      = f_in * E / E_0 - (q / v) * f_out                              : init=1, min=0.01
        tau*dv/dt      = f_in - f_out                                                  : init=1, min=0.01
        f_out          = v**(1 / alpha)                                                : init=1, min=0.01

        # Revised coeeficients
        k_1            = 4.3 * v_0 * E_0 * TE
        k_2            = epsilon * r_0 * E_0 * TE
        k_3            = 1 - epsilon

        # Linear equation
        BOLD           = V_0 * ((k_1 + k_2) * (1 - q) + (k_3 - k_2) * (1 - v))         : init=0
    """
    name = "BOLD model RL"
    description = "BOLD computation with revised coefficients and linear BOLD equation (Stephan et al., 2007)."

    BoldModel.__init__(self, 
        parameters=parameters, 
        equations=equations,  
        inputs="I_CBF",
        output="BOLD",
        name=name, description=description
    )

ANNarchy.extensions.bold.balloon_CL #

Bases: BoldModel

A balloon model with classical coefficients and linear BOLD equation derived from Stephan et al. (2007).

Equivalent code:

balloon_CL = BoldModel(
    parameters = '''
        second    = 1000.0
        phi       = 1.0
        kappa     = 1/1.54
        gamma     = 1/2.46
        E_0       = 0.34
        tau       = 0.98
        alpha     = 0.33
        V_0       = 0.02
        v_0       = 40.3
        TE        = 40/1000.
        epsilon   = 1.43
    ''',
    equations = '''
        # Single input
        I_CBF          = sum(I_CBF)                                                : init=0
        ds/dt          = (phi * I_CBF - kappa * s - gamma * (f_in - 1))/second     : init=0
        df_in/dt       = s / second                                                : init=1, min=0.01

        E              = 1 - (1 - E_0)**(1 / f_in)                                 : init=0.3424
        dq/dt          = (f_in * E / E_0 - (q / v) * f_out)/(tau*second)           : init=1, min=0.01
        dv/dt          = (f_in - f_out)/(tau*second)                               : init=1, min=0.01
        f_out          = v**(1 / alpha)                                            : init=1, min=0.01

        # Classic coefficients
        k_1            = (1 - V_0) * 4.3 * v_0 * E_0 * TE
        k_2            = 2 * E_0
        k_3            = 1 - epsilon

        # Linear equation
        BOLD           = V_0 * ((k_1 + k_2) * (1 - q) + (k_3 - k_2) * (1 - v))        : init=0
    ''',
    inputs="I_CBF",
)
Source code in ANNarchy/extensions/bold/PredefinedModels.py
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
class balloon_CL(BoldModel):
    """
    A balloon model with classical coefficients and linear BOLD equation derived from Stephan et al. (2007).

    Equivalent code:

    ```python
    balloon_CL = BoldModel(
        parameters = '''
            second    = 1000.0
            phi       = 1.0
            kappa     = 1/1.54
            gamma     = 1/2.46
            E_0       = 0.34
            tau       = 0.98
            alpha     = 0.33
            V_0       = 0.02
            v_0       = 40.3
            TE        = 40/1000.
            epsilon   = 1.43
        ''',
        equations = '''
            # Single input
            I_CBF          = sum(I_CBF)                                                : init=0
            ds/dt          = (phi * I_CBF - kappa * s - gamma * (f_in - 1))/second     : init=0
            df_in/dt       = s / second                                                : init=1, min=0.01

            E              = 1 - (1 - E_0)**(1 / f_in)                                 : init=0.3424
            dq/dt          = (f_in * E / E_0 - (q / v) * f_out)/(tau*second)           : init=1, min=0.01
            dv/dt          = (f_in - f_out)/(tau*second)                               : init=1, min=0.01
            f_out          = v**(1 / alpha)                                            : init=1, min=0.01

            # Classic coefficients
            k_1            = (1 - V_0) * 4.3 * v_0 * E_0 * TE
            k_2            = 2 * E_0
            k_3            = 1 - epsilon

            # Linear equation
            BOLD           = V_0 * ((k_1 + k_2) * (1 - q) + (k_3 - k_2) * (1 - v))        : init=0
        ''',
        inputs="I_CBF",
    )
    ```
    """
    def __init__(self,
            phi       = 1.0,
            kappa     = 1/1.54,
            gamma     = 1/2.46,
            E_0       = 0.34,
            tau       = 0.98,
            alpha     = 0.33,
            V_0       = 0.02,
            v_0       = 40.3,
            TE        = 40/1000.,
            epsilon   = 1.43,
        ):

        """
        :param phi:       input coefficient
        :param kappa:     signal decay
        :param gamma:     feedback regulation
        :param E_0:       oxygen extraction fraction at rest
        :param tau:       time constant (in s!)
        :param alpha:     vessel stiffness
        :param V_0:       resting venous blood volume fraction
        :param v_0:       frequency offset at the outer surface of the magnetized vessel for fully deoxygenated blood at 1.5 T
        :param TE:        echo time
        :param epsilon:   ratio of intra- and extravascular signal
        """
        parameters = """
            second    = 1000.0 : population
            phi       = %(phi)s : population
            kappa     = %(kappa)s : population
            gamma     = %(gamma)s : population
            E_0       = %(E_0)s : population
            tau       = %(tau)s : population
            alpha     = %(alpha)s : population
            V_0       = %(V_0)s : population
            v_0       = %(v_0)s : population
            TE        = %(TE)s : population
            epsilon   = %(epsilon)s : population
        """ % {
            'phi': phi,
            'kappa': kappa,
            'gamma': gamma,
            'E_0': E_0,
            'tau': tau,
            'alpha': alpha,
            'V_0': V_0,
            'v_0': v_0,
            'TE': TE,
            'epsilon': epsilon,
        }

        equations = """
            # Single input
            I_CBF          = sum(I_CBF)                                                : init=0
            ds/dt          = (phi * I_CBF - kappa * s - gamma * (f_in - 1))/second     : init=0
            df_in/dt       = s / second                                                : init=1, min=0.01

            E              = 1 - (1 - E_0)**(1 / f_in)                                 : init=0.3424
            dq/dt          = (f_in * E / E_0 - (q / v) * f_out)/(tau*second)           : init=1, min=0.01
            dv/dt          = (f_in - f_out)/(tau*second)                               : init=1, min=0.01
            f_out          = v**(1 / alpha)                                            : init=1, min=0.01

            # Classic coefficients
            k_1            = (1 - V_0) * 4.3 * v_0 * E_0 * TE
            k_2            = 2 * E_0
            k_3            = 1 - epsilon

            # Linear equation
            BOLD           = V_0 * ((k_1 + k_2) * (1 - q) + (k_3 - k_2) * (1 - v))        : init=0
        """
        name = "BoldNeuron CL"
        description = "BOLD computation with classic coefficients and linear BOLD equation (Stephan et al., 2007)."

        BoldModel.__init__(self, 
            parameters=parameters, 
            equations=equations,  
            inputs="I_CBF",
            output="BOLD",
            name=name, description=description
        )

__init__(phi=1.0, kappa=1 / 1.54, gamma=1 / 2.46, E_0=0.34, tau=0.98, alpha=0.33, V_0=0.02, v_0=40.3, TE=40 / 1000.0, epsilon=1.43) #

Parameters:

  • phi

    input coefficient

  • kappa

    signal decay

  • gamma

    feedback regulation

  • E_0

    oxygen extraction fraction at rest

  • tau

    time constant (in s!)

  • alpha

    vessel stiffness

  • V_0

    resting venous blood volume fraction

  • v_0

    frequency offset at the outer surface of the magnetized vessel for fully deoxygenated blood at 1.5 T

  • TE

    echo time

  • epsilon

    ratio of intra- and extravascular signal

Source code in ANNarchy/extensions/bold/PredefinedModels.py
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
def __init__(self,
        phi       = 1.0,
        kappa     = 1/1.54,
        gamma     = 1/2.46,
        E_0       = 0.34,
        tau       = 0.98,
        alpha     = 0.33,
        V_0       = 0.02,
        v_0       = 40.3,
        TE        = 40/1000.,
        epsilon   = 1.43,
    ):

    """
    :param phi:       input coefficient
    :param kappa:     signal decay
    :param gamma:     feedback regulation
    :param E_0:       oxygen extraction fraction at rest
    :param tau:       time constant (in s!)
    :param alpha:     vessel stiffness
    :param V_0:       resting venous blood volume fraction
    :param v_0:       frequency offset at the outer surface of the magnetized vessel for fully deoxygenated blood at 1.5 T
    :param TE:        echo time
    :param epsilon:   ratio of intra- and extravascular signal
    """
    parameters = """
        second    = 1000.0 : population
        phi       = %(phi)s : population
        kappa     = %(kappa)s : population
        gamma     = %(gamma)s : population
        E_0       = %(E_0)s : population
        tau       = %(tau)s : population
        alpha     = %(alpha)s : population
        V_0       = %(V_0)s : population
        v_0       = %(v_0)s : population
        TE        = %(TE)s : population
        epsilon   = %(epsilon)s : population
    """ % {
        'phi': phi,
        'kappa': kappa,
        'gamma': gamma,
        'E_0': E_0,
        'tau': tau,
        'alpha': alpha,
        'V_0': V_0,
        'v_0': v_0,
        'TE': TE,
        'epsilon': epsilon,
    }

    equations = """
        # Single input
        I_CBF          = sum(I_CBF)                                                : init=0
        ds/dt          = (phi * I_CBF - kappa * s - gamma * (f_in - 1))/second     : init=0
        df_in/dt       = s / second                                                : init=1, min=0.01

        E              = 1 - (1 - E_0)**(1 / f_in)                                 : init=0.3424
        dq/dt          = (f_in * E / E_0 - (q / v) * f_out)/(tau*second)           : init=1, min=0.01
        dv/dt          = (f_in - f_out)/(tau*second)                               : init=1, min=0.01
        f_out          = v**(1 / alpha)                                            : init=1, min=0.01

        # Classic coefficients
        k_1            = (1 - V_0) * 4.3 * v_0 * E_0 * TE
        k_2            = 2 * E_0
        k_3            = 1 - epsilon

        # Linear equation
        BOLD           = V_0 * ((k_1 + k_2) * (1 - q) + (k_3 - k_2) * (1 - v))        : init=0
    """
    name = "BoldNeuron CL"
    description = "BOLD computation with classic coefficients and linear BOLD equation (Stephan et al., 2007)."

    BoldModel.__init__(self, 
        parameters=parameters, 
        equations=equations,  
        inputs="I_CBF",
        output="BOLD",
        name=name, description=description
    )

ANNarchy.extensions.bold.balloon_CN #

Bases: BoldModel

A balloon model with classic coefficients and non-linear BOLD equation derived from Stephan et al. (2007).

Equivalent code:

balloon_CN = BoldModel(
    parameters = '''
        second    = 1000.0
        phi       = 1.0
        kappa     = 1/1.54
        gamma     = 1/2.46
        E_0       = 0.34
        tau       = 0.98
        alpha     = 0.33
        V_0       = 0.02
        v_0       = 40.3
        TE        = 40/1000.
        epsilon   = 1.43
    ''',
    equations = '''
        # Single input
        I_CBF          = sum(I_CBF)                                                : init=0
        ds/dt          = (phi * I_CBF - kappa * s - gamma * (f_in - 1))/second     : init=0
        df_in/dt       = s / second                                                : init=1, min=0.01

        E              = 1 - (1 - E_0)**(1 / f_in)                                 : init=0.3424
        dq/dt          = (f_in * E / E_0 - (q / v) * f_out)/(tau*second)           : init=1, min=0.01
        dv/dt          = (f_in - f_out)/(tau*second)                               : init=1, min=0.01
        f_out          = v**(1 / alpha)                                            : init=1, min=0.01

        # Classic coefficients
        k_1            = (1 - V_0) * 4.3 * v_0 * E_0 * TE
        k_2            = 2 * E_0
        k_3            = 1 - epsilon

        # Non-linear equation
        BOLD           = V_0 * (k_1 * (1 - q) + k_2 * (1 - (q / v)) + k_3 * (1 - v))  : init=0
    ''',
    inputs="I_CBF",
)
Source code in ANNarchy/extensions/bold/PredefinedModels.py
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
class balloon_CN(BoldModel):
    """
    A balloon model with classic coefficients and non-linear BOLD equation derived from Stephan et al. (2007).

    Equivalent code:

    ```python
    balloon_CN = BoldModel(
        parameters = '''
            second    = 1000.0
            phi       = 1.0
            kappa     = 1/1.54
            gamma     = 1/2.46
            E_0       = 0.34
            tau       = 0.98
            alpha     = 0.33
            V_0       = 0.02
            v_0       = 40.3
            TE        = 40/1000.
            epsilon   = 1.43
        ''',
        equations = '''
            # Single input
            I_CBF          = sum(I_CBF)                                                : init=0
            ds/dt          = (phi * I_CBF - kappa * s - gamma * (f_in - 1))/second     : init=0
            df_in/dt       = s / second                                                : init=1, min=0.01

            E              = 1 - (1 - E_0)**(1 / f_in)                                 : init=0.3424
            dq/dt          = (f_in * E / E_0 - (q / v) * f_out)/(tau*second)           : init=1, min=0.01
            dv/dt          = (f_in - f_out)/(tau*second)                               : init=1, min=0.01
            f_out          = v**(1 / alpha)                                            : init=1, min=0.01

            # Classic coefficients
            k_1            = (1 - V_0) * 4.3 * v_0 * E_0 * TE
            k_2            = 2 * E_0
            k_3            = 1 - epsilon

            # Non-linear equation
            BOLD           = V_0 * (k_1 * (1 - q) + k_2 * (1 - (q / v)) + k_3 * (1 - v))  : init=0
        ''',
        inputs="I_CBF",
    )
    ```
    """
    def __init__(self,
            phi       = 1.0,
            kappa     = 1/1.54,
            gamma     = 1/2.46,
            E_0       = 0.34,
            tau       = 0.98,
            alpha     = 0.33,
            V_0       = 0.02,
            v_0       = 40.3,
            TE        = 40/1000.,
            epsilon   = 1.43,
        ):

        """
        :param phi:       input coefficient
        :param kappa:     signal decay
        :param gamma:     feedback regulation
        :param E_0:       oxygen extraction fraction at rest
        :param tau:       time constant (in s!)
        :param alpha:     vessel stiffness
        :param V_0:       resting venous blood volume fraction
        :param v_0:       frequency offset at the outer surface of the magnetized vessel for fully deoxygenated blood at 1.5 T
        :param TE:        echo time
        :param epsilon:   ratio of intra- and extravascular signal
        """
        parameters = """
            second    = 1000.0 : population
            phi       = %(phi)s : population
            kappa     = %(kappa)s : population
            gamma     = %(gamma)s : population
            E_0       = %(E_0)s : population
            tau       = %(tau)s : population
            alpha     = %(alpha)s : population
            V_0       = %(V_0)s : population
            v_0       = %(v_0)s : population
            TE        = %(TE)s : population
            epsilon   = %(epsilon)s : population
        """ % {
            'phi': phi,
            'kappa': kappa,
            'gamma': gamma,
            'E_0': E_0,
            'tau': tau,
            'alpha': alpha,
            'V_0': V_0,
            'v_0': v_0,
            'TE': TE,
            'epsilon': epsilon,
        }

        equations = """
            # Single input
            I_CBF          = sum(I_CBF)                                                : init=0
            ds/dt          = (phi * I_CBF - kappa * s - gamma * (f_in - 1))/second     : init=0
            df_in/dt       = s / second                                                : init=1, min=0.01

            E              = 1 - (1 - E_0)**(1 / f_in)                                 : init=0.3424
            dq/dt          = (f_in * E / E_0 - (q / v) * f_out)/(tau*second)           : init=1, min=0.01
            dv/dt          = (f_in - f_out)/(tau*second)                               : init=1, min=0.01
            f_out          = v**(1 / alpha)                                            : init=1, min=0.01

            # Classic coefficients
            k_1            = (1 - V_0) * 4.3 * v_0 * E_0 * TE
            k_2            = 2 * E_0
            k_3            = 1 - epsilon

            # Non-linear equation
            BOLD           = V_0 * (k_1 * (1 - q) + k_2 * (1 - (q / v)) + k_3 * (1 - v))  : init=0
        """
        name = "BoldNeuron CN",
        description = "BOLD computation with classic coefficients and non-linear BOLD equation (Stephan et al., 2007)."

        BoldModel.__init__(self, 
            parameters=parameters, 
            equations=equations,  
            inputs="I_CBF",
            output="BOLD",
            name=name, description=description
        )

__init__(phi=1.0, kappa=1 / 1.54, gamma=1 / 2.46, E_0=0.34, tau=0.98, alpha=0.33, V_0=0.02, v_0=40.3, TE=40 / 1000.0, epsilon=1.43) #

Parameters:

  • phi

    input coefficient

  • kappa

    signal decay

  • gamma

    feedback regulation

  • E_0

    oxygen extraction fraction at rest

  • tau

    time constant (in s!)

  • alpha

    vessel stiffness

  • V_0

    resting venous blood volume fraction

  • v_0

    frequency offset at the outer surface of the magnetized vessel for fully deoxygenated blood at 1.5 T

  • TE

    echo time

  • epsilon

    ratio of intra- and extravascular signal

Source code in ANNarchy/extensions/bold/PredefinedModels.py
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
def __init__(self,
        phi       = 1.0,
        kappa     = 1/1.54,
        gamma     = 1/2.46,
        E_0       = 0.34,
        tau       = 0.98,
        alpha     = 0.33,
        V_0       = 0.02,
        v_0       = 40.3,
        TE        = 40/1000.,
        epsilon   = 1.43,
    ):

    """
    :param phi:       input coefficient
    :param kappa:     signal decay
    :param gamma:     feedback regulation
    :param E_0:       oxygen extraction fraction at rest
    :param tau:       time constant (in s!)
    :param alpha:     vessel stiffness
    :param V_0:       resting venous blood volume fraction
    :param v_0:       frequency offset at the outer surface of the magnetized vessel for fully deoxygenated blood at 1.5 T
    :param TE:        echo time
    :param epsilon:   ratio of intra- and extravascular signal
    """
    parameters = """
        second    = 1000.0 : population
        phi       = %(phi)s : population
        kappa     = %(kappa)s : population
        gamma     = %(gamma)s : population
        E_0       = %(E_0)s : population
        tau       = %(tau)s : population
        alpha     = %(alpha)s : population
        V_0       = %(V_0)s : population
        v_0       = %(v_0)s : population
        TE        = %(TE)s : population
        epsilon   = %(epsilon)s : population
    """ % {
        'phi': phi,
        'kappa': kappa,
        'gamma': gamma,
        'E_0': E_0,
        'tau': tau,
        'alpha': alpha,
        'V_0': V_0,
        'v_0': v_0,
        'TE': TE,
        'epsilon': epsilon,
    }

    equations = """
        # Single input
        I_CBF          = sum(I_CBF)                                                : init=0
        ds/dt          = (phi * I_CBF - kappa * s - gamma * (f_in - 1))/second     : init=0
        df_in/dt       = s / second                                                : init=1, min=0.01

        E              = 1 - (1 - E_0)**(1 / f_in)                                 : init=0.3424
        dq/dt          = (f_in * E / E_0 - (q / v) * f_out)/(tau*second)           : init=1, min=0.01
        dv/dt          = (f_in - f_out)/(tau*second)                               : init=1, min=0.01
        f_out          = v**(1 / alpha)                                            : init=1, min=0.01

        # Classic coefficients
        k_1            = (1 - V_0) * 4.3 * v_0 * E_0 * TE
        k_2            = 2 * E_0
        k_3            = 1 - epsilon

        # Non-linear equation
        BOLD           = V_0 * (k_1 * (1 - q) + k_2 * (1 - (q / v)) + k_3 * (1 - v))  : init=0
    """
    name = "BoldNeuron CN",
    description = "BOLD computation with classic coefficients and non-linear BOLD equation (Stephan et al., 2007)."

    BoldModel.__init__(self, 
        parameters=parameters, 
        equations=equations,  
        inputs="I_CBF",
        output="BOLD",
        name=name, description=description
    )

ANNarchy.extensions.bold.balloon_maith2021 #

Bases: BoldModel

The balloon model as used in Maith et al. (2021).

Source code in ANNarchy/extensions/bold/PredefinedModels.py
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
class balloon_maith2021(BoldModel):
    """
    The balloon model as used in Maith et al. (2021).
    """
    def __init__(self):
        "Constructor"

        parameters = """
            second    = 1000.0
            phi   = 1.0
            kappa = 0.665
            gamma = 0.412
            E_0       = 0.3424
            tau     = 1.0368
            alpha     = 0.3215
            V_0       = 0.02
        """
        equations = """
            I_CBF          = sum(I_CBF)                                                 : init=0
            ds/dt          = (phi * I_CBF - kappa * s - gamma * (f_in - 1))/second      : init=0
            df_in/dt       = s / second                                                 : init=1, min=0.01

            E              = 1 - (1 - E_0)**(1 / f_in)                                  : init=0.3424
            dq/dt          = (f_in * E / E_0 - (q / v) * f_out)/(tau*second)            : init=1, min=0.01
            dv/dt          = (f_in - f_out)/(tau*second)                                : init=1, min=0.01
            f_out          = v**(1 / alpha)                                             : init=1, min=0.01

            k_1            = 7 * E_0
            k_2            = 2
            k_3            = 2 * E_0 - 0.2

            BOLD           = V_0 * (k_1 * (1 - q) + k_2 * (1 - (q / v)) + k_3 * (1 - v)) : init=0
        """
        name = "Maith2021 BOLD model"
        description = "BOLD computation from Maith et al. (2021)."

        BoldModel.__init__(self, 
            parameters=parameters, 
            equations=equations,  
            inputs="I_CBF",
            output="BOLD",
            name=name, description=description
        )

__init__() #

Constructor

Source code in ANNarchy/extensions/bold/PredefinedModels.py
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
def __init__(self):
    "Constructor"

    parameters = """
        second    = 1000.0
        phi   = 1.0
        kappa = 0.665
        gamma = 0.412
        E_0       = 0.3424
        tau     = 1.0368
        alpha     = 0.3215
        V_0       = 0.02
    """
    equations = """
        I_CBF          = sum(I_CBF)                                                 : init=0
        ds/dt          = (phi * I_CBF - kappa * s - gamma * (f_in - 1))/second      : init=0
        df_in/dt       = s / second                                                 : init=1, min=0.01

        E              = 1 - (1 - E_0)**(1 / f_in)                                  : init=0.3424
        dq/dt          = (f_in * E / E_0 - (q / v) * f_out)/(tau*second)            : init=1, min=0.01
        dv/dt          = (f_in - f_out)/(tau*second)                                : init=1, min=0.01
        f_out          = v**(1 / alpha)                                             : init=1, min=0.01

        k_1            = 7 * E_0
        k_2            = 2
        k_3            = 2 * E_0 - 0.2

        BOLD           = V_0 * (k_1 * (1 - q) + k_2 * (1 - (q / v)) + k_3 * (1 - v)) : init=0
    """
    name = "Maith2021 BOLD model"
    description = "BOLD computation from Maith et al. (2021)."

    BoldModel.__init__(self, 
        parameters=parameters, 
        equations=equations,  
        inputs="I_CBF",
        output="BOLD",
        name=name, description=description
    )

ANNarchy.extensions.bold.balloon_two_inputs #

Bases: BoldModel

BOLD model with two input signals (CBF-driving and CMRO2-driving) for the ballon model and non-linear BOLD equation with revised coefficients based on Buxton et al. (2004), Friston et al. (2000) and Stephan et al. (2007).

Source code in ANNarchy/extensions/bold/PredefinedModels.py
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
class balloon_two_inputs(BoldModel):
    """
    BOLD model with two input signals (CBF-driving and CMRO2-driving) for the ballon model and non-linear BOLD equation with revised coefficients based on Buxton et al. (2004), Friston et al. (2000) and Stephan et al. (2007).
    """
    def __init__(self):
        "Constructor"

        # damped harmonic oscillators, gamma->spring coefficient, kappa->damping coefficient
        # CBF --> gamma from Friston
        # CMRO2 --> faster --> gamma=gamma_CBF*10 (therefore scaling of I_CMRO2 by (gamma_CMRO2 / gamma_CBF) --> if same input (I_CBF==I_CMRO2) CMRO2 and CBF same steady-state)
        # critical kappa --> kappa**2-4*gamma = 0 --> kappa=sqrt(4*gamma)
        # CBF underdamped for undershoot --> kappa = 0.6*sqrt(4*gamma)
        # CMRO2 critical --> kappa = sqrt(4*gamma)
        # after CBF and CMRO2 standard balloon model with revised coefficients, parameter values = Friston et al. (2000)
        parameters = """
            kappa_CBF   = 0.7650920556760059
            gamma_CBF   = 1/2.46
            kappa_CMRO2 = 4.032389192727559
            gamma_CMRO2 = 10/2.46
            phi_CBF     = 1.0
            phi_CMRO2   = 1.0
            E_0         = 0.34
            tau         = 0.98
            alpha       = 0.33
            V_0         = 0.02
            v_0         = 40.3
            TE          = 40/1000.
            epsilon     = 1
            r_0         = 25
            tau_out1    = 0
            tau_out2    = 20
            second      = 1000
        """
        equations = """
            # CBF input
            I_CBF               = sum(I_CBF)                                                   : init=0
            second*ds_CBF/dt    = phi_CBF * I_CBF - kappa_CBF * s_CBF - gamma_CBF * (f_in - 1) : init=0
            second*df_in/dt     = s_CBF                                                        : init=1, min=0.01

            # CMRO2 input
            I_CMRO2             = sum(I_CMRO2)                                                 : init=0
            second*ds_CMRO2/dt  = phi_CMRO2 * I_CMRO2 * (gamma_CMRO2 / gamma_CBF) - kappa_CMRO2 * s_CMRO2 - gamma_CMRO2 * (r - 1) : init=0
            second*dr/dt        = s_CMRO2                                                      : init=1, min=0.01

            dv                  = f_in - v**(1 / alpha)
            tau_out             = if dv>0: tau_out1 else: tau_out2
            f_out               = v**(1/alpha) + tau_out * dv / (tau + tau_out)              : init=1, min=0.01

            dq/dt               = (r - (q / v) * f_out)  / (second*tau)                        : init=1, min=0.01
            dv/dt               = dv / (tau + tau_out)  / second                             : init=1, min=0.01

            # Revised coefficients
            k_1                 = 4.3 * v_0 * E_0 * TE
            k_2                 = epsilon * r_0 * E_0 * TE
            k_3                 = 1 - epsilon

            # Non-linear equation
            BOLD                = V_0 * (k_1 * (1 - q) + k_2 * (1 - (q / v)) + k_3 * (1 - v))  : init=0
        """
        name = "BOLD model with two inputs"
        description = "BOLD model with two inputs (CBF-driving and CMRO2-driving). Combination of neurovascular coupling of Friston et al. (2000) and non-linear Balloon model with revised coefficients (Buxton et al, 1998, Stephan et al, 2007)"

        BoldModel.__init__(self, 
            parameters=parameters, 
            equations=equations, 
            inputs=['I_CBF', 'I_CMRO2'],
            output="BOLD",
            name=name, 
            description=description
        )

__init__() #

Constructor

Source code in ANNarchy/extensions/bold/PredefinedModels.py
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
def __init__(self):
    "Constructor"

    # damped harmonic oscillators, gamma->spring coefficient, kappa->damping coefficient
    # CBF --> gamma from Friston
    # CMRO2 --> faster --> gamma=gamma_CBF*10 (therefore scaling of I_CMRO2 by (gamma_CMRO2 / gamma_CBF) --> if same input (I_CBF==I_CMRO2) CMRO2 and CBF same steady-state)
    # critical kappa --> kappa**2-4*gamma = 0 --> kappa=sqrt(4*gamma)
    # CBF underdamped for undershoot --> kappa = 0.6*sqrt(4*gamma)
    # CMRO2 critical --> kappa = sqrt(4*gamma)
    # after CBF and CMRO2 standard balloon model with revised coefficients, parameter values = Friston et al. (2000)
    parameters = """
        kappa_CBF   = 0.7650920556760059
        gamma_CBF   = 1/2.46
        kappa_CMRO2 = 4.032389192727559
        gamma_CMRO2 = 10/2.46
        phi_CBF     = 1.0
        phi_CMRO2   = 1.0
        E_0         = 0.34
        tau         = 0.98
        alpha       = 0.33
        V_0         = 0.02
        v_0         = 40.3
        TE          = 40/1000.
        epsilon     = 1
        r_0         = 25
        tau_out1    = 0
        tau_out2    = 20
        second      = 1000
    """
    equations = """
        # CBF input
        I_CBF               = sum(I_CBF)                                                   : init=0
        second*ds_CBF/dt    = phi_CBF * I_CBF - kappa_CBF * s_CBF - gamma_CBF * (f_in - 1) : init=0
        second*df_in/dt     = s_CBF                                                        : init=1, min=0.01

        # CMRO2 input
        I_CMRO2             = sum(I_CMRO2)                                                 : init=0
        second*ds_CMRO2/dt  = phi_CMRO2 * I_CMRO2 * (gamma_CMRO2 / gamma_CBF) - kappa_CMRO2 * s_CMRO2 - gamma_CMRO2 * (r - 1) : init=0
        second*dr/dt        = s_CMRO2                                                      : init=1, min=0.01

        dv                  = f_in - v**(1 / alpha)
        tau_out             = if dv>0: tau_out1 else: tau_out2
        f_out               = v**(1/alpha) + tau_out * dv / (tau + tau_out)              : init=1, min=0.01

        dq/dt               = (r - (q / v) * f_out)  / (second*tau)                        : init=1, min=0.01
        dv/dt               = dv / (tau + tau_out)  / second                             : init=1, min=0.01

        # Revised coefficients
        k_1                 = 4.3 * v_0 * E_0 * TE
        k_2                 = epsilon * r_0 * E_0 * TE
        k_3                 = 1 - epsilon

        # Non-linear equation
        BOLD                = V_0 * (k_1 * (1 - q) + k_2 * (1 - (q / v)) + k_3 * (1 - v))  : init=0
    """
    name = "BOLD model with two inputs"
    description = "BOLD model with two inputs (CBF-driving and CMRO2-driving). Combination of neurovascular coupling of Friston et al. (2000) and non-linear Balloon model with revised coefficients (Buxton et al, 1998, Stephan et al, 2007)"

    BoldModel.__init__(self, 
        parameters=parameters, 
        equations=equations, 
        inputs=['I_CBF', 'I_CMRO2'],
        output="BOLD",
        name=name, 
        description=description
    )

References#

Buxton, R. B., Wong, E. C., and Frank, L. R. (1998). Dynamics of blood flow and oxygenation changes during brain activation: the balloon model. Magnetic resonance in medicine 39, 855–864. doi:10.1002/mrm.1910390602

Friston et al. (2000). Nonlinear responses in fMRI: the balloon model, volterra kernels, and other hemodynamics. NeuroImage 12, 466–477

Buxton et al. (2004). Modeling the hemodynamic response to brain activation. Neuroimage 23, S220–S233. doi:10.1016/j.neuroimage.2004.07.013

Stephan et al. (2007). Comparing hemodynamic models with DCM. Neuroimage 38, 387–401. doi:10.1016/j.neuroimage.2007.07.040

Maith et al. (2021) A computational model-based analysis of basal ganglia pathway changes in Parkinson’s disease inferred from resting-state fMRI. European Journal of Neuroscience. 2021; 53: 2278– 2295. doi:10.1111/ejn.14868

Maith et al. (2022) BOLD monitoring in the neural simulator ANNarchy. Frontiers in Neuroinformatics 16. doi:10.3389/fninf.2022.790966.