예제 #1
0
 def setup_method(self):
     oscillator = models.Generic2dOscillator()
     white_matter = connectivity.Connectivity.from_file(
         'connectivity_%d.zip' % (self.n_regions, ))
     white_matter.speed = numpy.array([self.speed])
     white_matter_coupling = coupling.Difference(a=self.coupling_a)
     heunint = integrators.HeunStochastic(
         dt=2**-4, noise=noise.Additive(nsig=numpy.array([
             2**-10,
         ])))
     mons = (
         monitors.EEG.from_file(period=self.period),
         monitors.MEG.from_file(period=self.period),
         monitors.iEEG.from_file(period=self.period),
     )
     local_coupling_strength = numpy.array([2**-10])
     region_mapping = RegionMapping.from_file('regionMapping_16k_%d.txt' %
                                              (self.n_regions, ))
     default_cortex = Cortex(region_mapping_data=region_mapping,
                             load_default=True)
     default_cortex.coupling_strength = local_coupling_strength
     self.sim = simulator.Simulator(model=oscillator,
                                    connectivity=white_matter,
                                    coupling=white_matter_coupling,
                                    integrator=heunint,
                                    monitors=mons,
                                    surface=default_cortex)
     self.sim.configure()
예제 #2
0
    def configure(self,
                  dt=2**-3,
                  model=models.Generic2dOscillator,
                  speed=4.0,
                  coupling_strength=0.00042,
                  method="HeunDeterministic",
                  surface_sim=False,
                  default_connectivity=True):
        """
        Create an instance of the Simulator class, by default use the
        generic plane oscillator local dynamic model and the deterministic 
        version of Heun's method for the numerical integration.
        
        """
        self.method = method

        if default_connectivity:
            white_matter = connectivity.Connectivity(load_default=True)
            # NOTE: This is the default region mapping should consider changing the name.
            region_mapping = RegionMapping.from_file(
                source_file=
                "cortex_reg13/region_mapping/o52r00_irp2008_hemisphere_both_subcortical_false_regions_74.txt.bz2"
            )
        else:
            white_matter = connectivity.Connectivity.from_file(
                source_file="connectivity_190.zip")
            region_mapping = RegionMapping.from_file(
                source_file=
                "cortex_reg13/region_mapping/o52r00_irp2008_hemisphere_both_subcortical_true_regions_190.txt.bz2"
            )

        white_matter_coupling = coupling.Linear(a=coupling_strength)
        white_matter.speed = speed

        dynamics = model()

        if method[-10:] == "Stochastic":
            hisss = noise.Additive(nsig=numpy.array([2**-11]))
            integrator = eval("integrators." + method + "(dt=dt, noise=hisss)")
        else:
            integrator = eval("integrators." + method + "(dt=dt)")

        if surface_sim:
            local_coupling_strength = numpy.array([2**-10])
            default_cortex = Cortex(load_default=True,
                                    region_mapping_data=region_mapping)
            default_cortex.coupling_strength = local_coupling_strength
            default_cortex.local_connectivity = LocalConnectivity(
                load_default=default_connectivity, surface=default_cortex)
        else:
            default_cortex = None

        # Order of monitors determines order of returned values.
        self.sim = simulator.Simulator(model=dynamics,
                                       connectivity=white_matter,
                                       coupling=white_matter_coupling,
                                       integrator=integrator,
                                       monitors=self.monitors,
                                       surface=default_cortex)
        self.sim.configure()
예제 #3
0
 def make_cortex(self, local_connectivity=None, coupling_strength=None):
     self._cortex = Cortex()
     self._cortex.region_mapping_data = self.cortical_region_mapping
     if isinstance(local_connectivity, LocalConnectivity):
         self._cortex.local_connectivity = local_connectivity
     if coupling_strength is not None:
         self._cortex.coupling_strength = coupling_strength
     self._cortex.configure()
     return self._cortex
예제 #4
0
    def test_assign_complex_attr(self):
        """
        Test scientific methods are executed
        """
        default_cortex = Cortex.from_file()
        default_cortex.coupling_strength = 0.0121
        self.assertTrue(default_cortex.local_connectivity is None)

        #default_cortex.local_connectivity = surfaces.LocalConnectivity(cutoff=2, surface=default_cortex)
        #default_cortex.compute_local_connectivity()
        #self.assertTrue(default_cortex.local_connectivity is not None)

        default_lc = LocalConnectivity(load_default=True, cutoff=2)
        other_cortex = Cortex(local_connectivity=default_lc)
        self.assertTrue(other_cortex.local_connectivity is not None)
예제 #5
0
    def test_assign_complex_attr(self):
        """
        Test scientific methods are executed
        """
        default_cortex = Cortex(load_file="cortex_16384.zip")
        default_cortex.coupling_strength = 0.0121
        assert default_cortex.local_connectivity is None

        # default_cortex.local_connectivity = surfaces.LocalConnectivity(cutoff=2, surface=default_cortex)
        # default_cortex.compute_local_connectivity()
        # self.assertTrue(default_cortex.local_connectivity is not None)

        default_lc = LocalConnectivity(
            cutoff=2, load_file="local_connectivity_16384.mat")
        other_cortex = Cortex(local_connectivity=default_lc)
        assert other_cortex.local_connectivity is not None
예제 #6
0
 def test_region_boundaries(self):
     cortex = Cortex.from_file()
     white_matter = connectivity.Connectivity(load_default=True)
     white_matter.configure()
     rb = region_boundaries.RegionBoundaries(cortex)
     assert len(
         rb.region_neighbours.keys()) == white_matter.number_of_regions
    def configure(self, dt=2 ** -3, model=models.Generic2dOscillator, speed=4.0,
                  coupling_strength=0.00042, method="HeunDeterministic",
                  surface_sim=False,
                  default_connectivity=True):
        """
        Create an instance of the Simulator class, by default use the
        generic plane oscillator local dynamic model and the deterministic
        version of Heun's method for the numerical integration.

        """
        self.method = method

        if default_connectivity:
            white_matter = Connectivity(load_file="connectivity_76.zip")
            region_mapping = RegionMapping(load_file="regionMapping_16k_76.txt")
        else:
            white_matter = Connectivity(load_file="connectivity_192.zip")
            region_mapping = RegionMapping(load_file="regionMapping_16k_192.txt")

        white_matter_coupling = coupling.Linear(a=coupling_strength)
        white_matter.speed = speed

        dynamics = model()

        if method[-10:] == "Stochastic":
            hisss = noise.Additive(nsig=numpy.array([2 ** -11]))
            integrator = eval("integrators." + method + "(dt=dt, noise=hisss)")
        else:
            integrator = eval("integrators." + method + "(dt=dt)")

        if surface_sim:
            local_coupling_strength = numpy.array([2 ** -10])
            default_cortex = Cortex(region_mapping_data=region_mapping, load_file="cortex_16384.zip")
            default_cortex.coupling_strength = local_coupling_strength
            default_cortex.local_connectivity = LocalConnectivity(load_file="local_connectivity_16384.mat")
        else:
            default_cortex = None

        # Order of monitors determines order of returned values.
        self.sim = simulator.Simulator(model=dynamics,
                                       connectivity=white_matter,
                                       coupling=white_matter_coupling,
                                       integrator=integrator,
                                       monitors=self.monitors,
                                       surface=default_cortex)
        self.sim.configure()
예제 #8
0
    def test_surface_sim_with_projections(self):

        # Setup Simulator obj
        oscillator = models.Generic2dOscillator()
        white_matter = connectivity.Connectivity.from_file('connectivity_%d.zip' % (self.n_regions,))
        white_matter.speed = numpy.array([self.speed])
        white_matter_coupling = coupling.Difference(a=self.coupling_a)
        heunint = integrators.HeunStochastic(
            dt=2 ** -4,
            noise=noise.Additive(nsig=numpy.array([2 ** -10, ]))
        )
        mons = (
            monitors.EEG.from_file(period=self.period),
            monitors.MEG.from_file(period=self.period),
            # monitors.iEEG.from_file(period=self.period),
            # SEEG projection data is not part of tvb-data on Pypi, thus this can not work generic
        )
        local_coupling_strength = numpy.array([2 ** -10])
        region_mapping = RegionMapping.from_file('regionMapping_16k_%d.txt' % (self.n_regions,))
        region_mapping.surface = CorticalSurface.from_file()
        default_cortex = Cortex.from_file()
        default_cortex.region_mapping_data = region_mapping
        default_cortex.coupling_strength = local_coupling_strength

        sim = simulator.Simulator(model=oscillator, connectivity=white_matter, coupling=white_matter_coupling,
                                  integrator=heunint, monitors=mons, surface=default_cortex)
        sim.configure()

        # check configured simulation connectivity attribute
        conn = sim.connectivity
        assert conn.number_of_regions == self.n_regions
        assert conn.speed == self.speed

        # test monitor properties
        lc_n_node = sim.surface.local_connectivity.matrix.shape[0]
        for mon in sim.monitors:
            assert mon.period == self.period
            n_sens, g_n_node = mon.gain.shape
            assert g_n_node == sim.number_of_nodes
            assert n_sens == mon.sensors.number_of_sensors
            assert lc_n_node == g_n_node

        # check output shape
        ys = {}
        mons = 'eeg meg seeg'.split()
        for key in mons:
            ys[key] = []
        for data in sim(simulation_length=3.0):
            for key, dat in zip(mons, data):
                if dat:
                    _, y = dat
                    ys[key].append(y)
        for mon, key in zip(sim.monitors, mons):
            ys[key] = numpy.array(ys[key])
            assert ys[key].shape[2] == mon.gain.shape[0]
예제 #9
0
    def test_cortex_reg_map_without_subcorticals(self):
        dt = Cortex.from_file()
        dt.region_mapping_data.connectivity = Connectivity.from_file()
        self.add_subcorticals_to_conn(dt.region_mapping_data.connectivity)
        dt.region_mapping_data.connectivity.configure()

        assert isinstance(dt, Cortex)
        assert dt.region_mapping is not None
        assert numpy.unique(
            dt.region_mapping
        ).size == dt.region_mapping_data.connectivity.number_of_regions
예제 #10
0
    def test_cortexdata(self):
        dt = Cortex.from_file(
            local_connectivity_file="local_connectivity_16384.mat")
        dt.region_mapping_data.connectivity = Connectivity.from_file()
        assert isinstance(dt, Cortex)
        assert dt.region_mapping is not None

        dt.configure()
        assert dt.vertices.shape == (16384, 3)
        assert dt.vertex_normals.shape == (16384, 3)
        assert dt.triangles.shape == (32760, 3)
예제 #11
0
    def _prepare_simulator_from_view_model(self, view_model):
        simulator = Simulator()
        simulator.gid = view_model.gid

        conn = self.load_traited_by_gid(view_model.connectivity)
        simulator.connectivity = conn

        simulator.conduction_speed = view_model.conduction_speed
        simulator.coupling = view_model.coupling

        rm_surface = None

        if view_model.surface:
            simulator.surface = Cortex()
            rm_index = self.load_entity_by_gid(
                view_model.surface.region_mapping_data.hex)
            rm = h5.load_from_index(rm_index)

            rm_surface_index = self.load_entity_by_gid(rm_index.fk_surface_gid)
            rm_surface = h5.load_from_index(rm_surface_index, CorticalSurface)
            rm.surface = rm_surface
            rm.connectivity = conn

            simulator.surface.region_mapping_data = rm
            if simulator.surface.local_connectivity:
                lc = self.load_traited_by_gid(
                    view_model.surface.local_connectivity)
                assert lc.surface.gid == rm_index.fk_surface_gid
                lc.surface = rm_surface
                simulator.surface.local_connectivity = lc

        if view_model.stimulus:
            stimulus_index = self.load_entity_by_gid(view_model.stimulus.hex)
            stimulus = h5.load_from_index(stimulus_index)
            simulator.stimulus = stimulus

            if isinstance(stimulus, StimuliSurface):
                simulator.stimulus.surface = rm_surface
            else:
                simulator.stimulus.connectivity = simulator.connectivity

        simulator.model = view_model.model
        simulator.integrator = view_model.integrator
        simulator.initial_conditions = view_model.initial_conditions
        simulator.monitors = view_model.monitors
        simulator.simulation_length = view_model.simulation_length

        # TODO: why not load history here?
        # if view_model.history:
        #     history_index = dao.get_datatype_by_gid(view_model.history.hex)
        #     history = h5.load_from_index(history_index)
        #     assert isinstance(history, SimulationHistory)
        #     history.fill_into(self.algorithm)
        return simulator
예제 #12
0
    def test_cortexdata(self):
        dt = Cortex.from_file()
        dt.__setattr__('valid_for_simulations', True)
        assert isinstance(dt, Cortex)
        assert dt.region_mapping is not None
        ## Initialize Local Connectivity, to avoid long computation time.
        dt.local_connectivity = LocalConnectivity.from_file()

        dt.configure()
        assert dt.vertices.shape == (16384, 3)
        assert dt.vertex_normals.shape == (16384, 3)
        assert dt.triangles.shape == (32760, 3)
예제 #13
0
 def setup_method(self):
     oscillator = models.Generic2dOscillator()
     white_matter = connectivity.Connectivity(load_file='connectivity_' +
                                              str(self.n_regions) + '.zip')
     white_matter.speed = numpy.array([self.speed])
     white_matter_coupling = coupling.Difference(a=self.coupling_a)
     heunint = integrators.HeunStochastic(
         dt=2**-4, noise=noise.Additive(nsig=numpy.array([
             2**-10,
         ])))
     mons = (
         monitors.EEG(projection=ProjectionMatrix(
             load_file='projection_eeg_65_surface_16k.npy'),
                      sensors=SensorsEEG(load_file="eeg_brainstorm_65.txt"),
                      period=self.period),
         monitors.MEG(
             projection=ProjectionMatrix(
                 load_file='projection_meg_276_surface_16k.npy'),
             sensors=SensorsMEG(load_file='meg_brainstorm_276.txt'),
             period=self.period),
         monitors.iEEG(projection=ProjectionMatrix(
             load_file='projection_seeg_588_surface_16k.npy'),
                       sensors=SensorsInternal(load_file='seeg_588.txt'),
                       period=self.period),
     )
     local_coupling_strength = numpy.array([2**-10])
     region_mapping = RegionMapping(load_file='regionMapping_16k_' +
                                    str(self.n_regions) + '.txt')
     default_cortex = Cortex(
         region_mapping_data=region_mapping, load_file="cortex_16384.zip"
     )  #region_mapping_file="regionMapping_16k_192.txt")
     default_cortex.coupling_strength = local_coupling_strength
     self.sim = simulator.Simulator(model=oscillator,
                                    connectivity=white_matter,
                                    coupling=white_matter_coupling,
                                    integrator=heunint,
                                    monitors=mons,
                                    surface=default_cortex)
     self.sim.configure()
예제 #14
0
    def test_cortexdata(self):
        dt = Cortex(load_default=True)
        assert isinstance(dt, Cortex)
        assert dt.region_mapping is not None
        ## Initialize Local Connectivity, to avoid long computation time.
        dt.local_connectivity = LocalConnectivity(load_default=True)

        dt.configure()
        summary_info = dt.summary_info
        assert abs(summary_info['Region area, maximum (mm:math:`^2`)'] -
                   9333.39) < 0.01
        assert abs(summary_info['Region area, mean (mm:math:`^2`)'] -
                   3038.51) < 0.01
        assert abs(summary_info['Region area, minimum (mm:math:`^2`)'] -
                   540.90) < 0.01
        assert dt.get_data_shape('vertices') == (16384, 3)
        assert dt.get_data_shape('vertex_normals') == (16384, 3)
        assert dt.get_data_shape('triangles') == (32760, 3)
예제 #15
0
 def cortex(self):
     cortex = Cortex()
     cortex.region_mapping_data = self.cortical_region_mapping
     cortex = cortex.populate_cortex(self.cortical_surface._tvb, {})
     for s_type, sensors in self.sensors.items():
         if isinstance(sensors, OrderedDict) and len(sensors) > 0:
             projection = sensors.values()[0]
             if projection is not None:
                 setattr(cortex, s_type.lower(), projection.projection_data)
     cortex.configure()
     return cortex
예제 #16
0
    def test_cortexdata(self):

        dt = Cortex(load_default=True)
        self.assertTrue(isinstance(dt, Cortex))
        self.assertTrue(dt.region_mapping is not None)
        ## Initialize Local Connectivity, to avoid long computation time.
        dt.local_connectivity = LocalConnectivity(load_default=True)

        dt.configure()
        summary_info = dt.summary_info
        self.assertTrue(
            abs(summary_info['Region area, maximum (mm:math:`^2`)'] -
                9119.4540365252615) < 0.00000001)
        self.assertTrue(
            abs(summary_info['Region area, mean (mm:math:`^2`)'] -
                3366.2542250541251) < 0.00000001)
        self.assertTrue(
            abs(summary_info['Region area, minimum (mm:math:`^2`)'] -
                366.48271886512993) < 0.00000001)
        self.assertEqual(dt.get_data_shape('vertices'), (16384, 3))
        self.assertEqual(dt.get_data_shape('vertex_normals'), (16384, 3))
        self.assertEqual(dt.get_data_shape('triangles'), (32760, 3))
예제 #17
0
    def test_cortexdata(self):
        dt = Cortex(load_file="cortex_16384.zip",
                    region_mapping_data=RegionMapping(
                        load_file="regionMapping_16k_76.txt"))
        assert isinstance(dt, Cortex)
        assert dt.region_mapping_data is not None
        ## Initialize Local Connectivity, to avoid long computation time.
        dt.local_connectivity = LocalConnectivity(
            load_file="local_connectivity_16384.mat")

        dt.configure()
        summary_info = dt._find_summary_info()
        assert abs(summary_info['Region area, maximum (mm:math:`^2`)'] -
                   9333.39) < 0.01
        assert abs(summary_info['Region area, mean (mm:math:`^2`)'] -
                   3038.51) < 0.01
        assert abs(summary_info['Region area, minimum (mm:math:`^2`)'] -
                   540.90) < 0.01
        assert dt.vertices.shape == (16384, 3)
        assert dt.vertex_normals.shape == (16384, 3)
        assert dt.triangles.shape == (32760, 3)
예제 #18
0
    def configure(self,
                  dt=2**-3,
                  model=ModelsEnum.GENERIC_2D_OSCILLATOR.get_class(),
                  speed=4.0,
                  coupling_strength=0.00042,
                  method=HeunDeterministic,
                  surface_sim=False,
                  default_connectivity=True,
                  with_stimulus=False):
        """
        Create an instance of the Simulator class, by default use the
        generic plane oscillator local dynamic model and the deterministic 
        version of Heun's method for the numerical integration.
        
        """
        self.method = method

        if default_connectivity:
            white_matter = Connectivity.from_file()
            region_mapping = RegionMapping.from_file(
                source_file="regionMapping_16k_76.txt")
        else:
            white_matter = Connectivity.from_file(
                source_file="connectivity_192.zip")
            region_mapping = RegionMapping.from_file(
                source_file="regionMapping_16k_192.txt")
        region_mapping.surface = CorticalSurface.from_file()

        white_matter_coupling = coupling.Linear(
            a=numpy.array([coupling_strength]))
        white_matter.speed = numpy.array(
            [speed])  # no longer allow scalars to numpy array promotion

        dynamics = model()

        if issubclass(method, IntegratorStochastic):
            hisss = noise.Additive(nsig=numpy.array([2**-11]))
            integrator = method(dt=dt, noise=hisss)
        else:
            integrator = method(dt=dt)

        if surface_sim:
            local_coupling_strength = numpy.array([2**-10])
            default_cortex = Cortex.from_file()
            default_cortex.region_mapping_data = region_mapping
            default_cortex.coupling_strength = local_coupling_strength
            if default_connectivity:
                default_cortex.local_connectivity = LocalConnectivity.from_file(
                )
            else:
                default_cortex.local_connectivity = LocalConnectivity()
            default_cortex.local_connectivity.surface = default_cortex.region_mapping_data.surface
            # TODO stimulus
        else:
            default_cortex = None
            if with_stimulus:
                weights = StimuliRegion.get_default_weights(
                    white_matter.weights.shape[0])
                weights[self.stim_nodes] = 1.
                stimulus = StimuliRegion(temporal=Linear(parameters={
                    "a": 0.0,
                    "b": self.stim_value
                }),
                                         connectivity=white_matter,
                                         weight=weights)

        # Order of monitors determines order of returned values.
        self.sim = simulator.Simulator()
        self.sim.surface = default_cortex
        self.sim.model = dynamics
        self.sim.integrator = integrator
        self.sim.connectivity = white_matter
        self.sim.coupling = white_matter_coupling
        self.sim.monitors = self.monitors
        if with_stimulus:
            self.sim.stimulus = stimulus
        self.sim.configure()
예제 #19
0
oscillator = models.Generic2dOscillator()
white_matter = connectivity.Connectivity.from_file('connectivity_192.zip')
white_matter.speed = numpy.array([4.0])
white_matter_coupling = coupling.Difference(a=0.014)
heunint = integrators.HeunStochastic(
    dt=2**-4,
    noise=noise.Additive(nsig=numpy.array([2 ** -10, ]))
)
fsamp = 1e3/1024.0 # 1024 Hz
monitors = (
    monitors.EEG.from_file('eeg-brainstorm-65.txt', 'projection_EEG_surface.npy', period=fsamp),
    monitors.MEG.from_file('meg-brainstorm-276.txt', 'projection_MEG_surface.npy', period=fsamp),
    monitors.iEEG.from_file('SEEG_588.txt', 'projection_SEEG_surface.npy', period=fsamp),
)
local_coupling_strength = numpy.array([2 ** -10])
default_cortex = Cortex(region_mapping_data=RegionMapping.from_file('regionMapping_16k_192.txt'),
                        load_default=True)
default_cortex.coupling_strength = local_coupling_strength
sim = simulator.Simulator(model=oscillator, connectivity=white_matter,
                          coupling=white_matter_coupling,
                          integrator=heunint, monitors=monitors,
                          surface=default_cortex)
sim.configure()

ts, ys = {}, {}
mons = 'eeg meg seeg'.split()
for key in mons:
    ts[key] = []
    ys[key] = []

for data in sim(simulation_length=2**2):
    for key, dat in zip(mons, data):
예제 #20
0
class Simulator(core.Type):
    """
    The Simulator class coordinates classes from all other modules in the
    simulator package in order to perform simulations. 

    In general, it is necessary to initialiaze a simulator with the desired
    components and then call the simulator in a loop to obtain simulation
    data:
    
    >>> sim = Simulator(...)
    >>> for output in sim(simulation_length=1000):
            ...
    
    Please refer to the user guide and the demos for more detail.


    .. #Currently there seems to be a clash betwen traits and autodoc, autodoc
    .. #can't find the methods of the class, the class specific names below get
    .. #us around this...
    .. automethod:: Simulator.__init__
    .. automethod:: Simulator.configure
    .. automethod:: Simulator.__call__
    .. automethod:: Simulator.configure_history
    .. automethod:: Simulator.configure_integrator_noise
    .. automethod:: Simulator.memory_requirement
    .. automethod:: Simulator.runtime
    .. automethod:: Simulator.storage_requirement


    """

    connectivity = connectivity_dtype.Connectivity(
        label="Long-range connectivity",
        default=None,
        order=1,
        required=True,
        filters_ui=[
            UIFilter(linked_elem_name="projection_matrix_data",
                     linked_elem_field=FilterChain.datatype + "._sources",
                     linked_elem_parent_name="monitors",
                     linked_elem_parent_option="EEG"),
            UIFilter(linked_elem_name="region_mapping_data",
                     linked_elem_field=FilterChain.datatype + "._connectivity",
                     linked_elem_parent_name="surface",
                     linked_elem_parent_option=None)
        ],
        doc="""A tvb.datatypes.Connectivity object which contains the
        structural long-range connectivity data (i.e., white-matter tracts). In
        combination with the ``Long-range coupling function`` it defines the inter-regional
        connections. These couplings undergo a time delay via signal propagation 
        with a propagation speed of ``Conduction Speed``""")

    conduction_speed = basic.Float(
        label="Conduction Speed",
        default=3.0,
        order=2,
        required=False,
        range=basic.Range(lo=0.01, hi=100.0, step=1.0),
        doc="""Conduction speed for ``Long-range connectivity`` (mm/ms)""")

    coupling = coupling_module.Coupling(
        label="Long-range coupling function",
        default=coupling_module.Linear(),
        required=True,
        order=2,
        doc="""The coupling function is applied to the activity propagated
        between regions by the ``Long-range connectivity`` before it enters the local
        dynamic equations of the Model. Its primary purpose is to 'rescale' the
        incoming activity to a level appropriate to Model.""")

    surface = Cortex(
        label="Cortical surface",
        default=None,
        order=3,
        required=False,
        filters_backend=FilterChain(
            fields=[FilterChain.datatype + '._valid_for_simulations'],
            operations=["=="],
            values=[True]),
        filters_ui=[
            UIFilter(linked_elem_name="projection_matrix_data",
                     linked_elem_field=FilterChain.datatype + "._sources",
                     linked_elem_parent_name="monitors",
                     linked_elem_parent_option="EEG"),
            UIFilter(linked_elem_name="local_connectivity",
                     linked_elem_field=FilterChain.datatype + "._surface",
                     linked_elem_parent_name="surface",
                     linked_elem_parent_option=None)
        ],
        doc="""By default, a tvb.datatypes.Cortex object which represents the
        cortical surface defined by points in the 3D physical space and their 
        neighborhood relationship. In the current TVB version, when setting up a 
        surface-based simulation, the option to configure the spatial spread of 
        the ``Local Connectivity`` is available.""")

    stimulus = patterns_dtype.SpatioTemporalPattern(
        label="Spatiotemporal stimulus",
        default=None,
        order=4,
        required=False,
        doc=
        """A ``Spatiotemporal stimulus`` can be defined at the region or surface level.
        It's composed of spatial and temporal components. For region defined stimuli
        the spatial component is just the strength with which the temporal
        component is applied to each region. For surface defined stimuli,  a
        (spatial) function, with finite-support, is used to define the strength 
        of the stimuli on the surface centred around one or more focal points. 
        In the current version of TVB, stimuli are applied to the first state 
        variable of the ``Local dynamic model``.""")

    model = models_module.Model(
        label="Local dynamic model",
        default=models_module.Generic2dOscillator,
        required=True,
        order=5,
        doc="""A tvb.simulator.Model object which describe the local dynamic
        equations, their parameters, and, to some extent, where connectivity
        (local and long-range) enters and which state-variables the Monitors
        monitor. By default the 'Generic2dOscillator' model is used. Read the 
        Scientific documentation to learn more about this model.""")

    integrator = integrators_module.Integrator(
        label="Integration scheme",
        default=integrators_module.HeunDeterministic,
        required=True,
        order=6,
        doc="""A tvb.simulator.Integrator object which is
            an integration scheme with supporting attributes such as 
            integration step size and noise specification for stochastic 
            methods. It is used to compute the time courses of the model state 
            variables.""")

    initial_conditions = arrays_dtype.FloatArray(
        label="Initial Conditions",
        default=None,
        order=-1,
        required=False,
        doc="""Initial conditions from which the simulation will begin. By
        default, random initial conditions are provided. Needs to be the same shape
        as simulator 'history', ie, initial history function which defines the 
        minimal initial state of the network with time delays before time t=0. 
        If the number of time points in the provided array is insufficient the 
        array will be padded with random values based on the 'state_variables_range'
        attribute.""")

    monitors = monitors_module.Monitor(
        label="Monitor(s)",
        default=monitors_module.TemporalAverage,
        required=True,
        order=8,
        select_multiple=True,
        doc="""A tvb.simulator.Monitor or a list of tvb.simulator.Monitor
        objects that 'know' how to record relevant data from the simulation. Two
        main types exist: 1) simple, spatial and temporal, reductions (subsets
        or averages); 2) physiological measurements, such as EEG, MEG and fMRI.
        By default the Model's specified variables_of_interest are returned,
        temporally downsampled from the raw integration rate to a sample rate of
        1024Hz.""")

    simulation_length = basic.Float(
        label="Simulation Length (ms)",
        default=1000.0,  # ie 1 second
        required=True,
        order=9,
        doc="""The length of a simulation in milliseconds (ms).""")

    def __init__(self, **kwargs):
        """
        Use the base class' mechanisms to initialise the traited attributes 
        declared above, overriding defaults with any provided keywords. Then
        declare any non-traited attributes.

        """
        super(Simulator, self).__init__(**kwargs)
        LOG.debug(str(kwargs))

        self.calls = 0
        self.current_step = 0

        self.number_of_nodes = None
        self.horizon = None
        self.good_history_shape = None
        self.history = None
        self._memory_requirement_guess = None
        self._memory_requirement_census = None
        self._storage_requirement = None
        self._runtime = None

    def __str__(self):
        return "Simulator(**kwargs)"

    def preconfigure(self):
        """
        Configure just the basic fields, so that memory can be estimated
        """
        self.connectivity.configure()

        if self.surface:
            self.surface.configure()

        if self.stimulus:
            self.stimulus.configure()

        self.coupling.configure()
        self.model.configure()
        self.integrator.configure()

        # monitors needs to be a list or tuple, even if there is only one...
        if not isinstance(self.monitors, (list, tuple)):
            self.monitors = [self.monitors]

        # Configure monitors
        for monitor in self.monitors:
            monitor.configure()

        ##------------- Now the the interdependant configuration -------------##

        #"Nodes" refers to either regions or vertices + non-cortical regions.
        if self.surface is None:
            self.number_of_nodes = self.connectivity.number_of_regions
        else:
            #try:
            self.number_of_nodes = self.surface.region_mapping.shape[0]
            #except AttributeError:
            #    msg = "%s: Surface needs region mapping defined... "
            #    LOG.error(msg % (repr(self)))

        # Estimate of memory usage
        self._guesstimate_memory_requirement()

    def configure(self, full_configure=True):
        """
        The first step of configuration is to run the configure methods of all
        the Simulator's components, ie its traited attributes.

        Configuration of a Simulator primarily consists of calculating the
        attributes, etc, which depend on the combinations of the Simulator's
        traited attributes (keyword args).

        Converts delays from physical time units into integration steps
        and updates attributes that depend on combinations of the 6 inputs.
        """
        if full_configure:
            # When run from GUI, preconfigure is run separately, and we want to avoid running that part twice
            self.preconfigure()

        #Make sure spatialised model parameters have the right shape (number_of_nodes, 1)
        excluded_params = ("state_variable_range", "variables_of_interest",
                           "noise", "psi_table", "nerf_table")

        for param in self.model.trait.keys():
            if param in excluded_params:
                continue
            #If it's a surface sim and model parameters were provided at the region level
            region_parameters = getattr(self.model, param)
            if self.surface is not None:
                if region_parameters.size == self.connectivity.number_of_regions:
                    new_parameters = region_parameters[
                        self.surface.region_mapping].reshape((-1, 1))
                    setattr(self.model, param, new_parameters)
            region_parameters = getattr(self.model, param)
            if region_parameters.size == self.number_of_nodes:
                new_parameters = region_parameters.reshape((-1, 1))
                setattr(self.model, param, new_parameters)

        #Configure spatial component of any stimuli
        self.configure_stimuli()

        #Set delays, provided in physical units, in integration steps.
        self.connectivity.set_idelays(self.integrator.dt)

        self.horizon = numpy.max(self.connectivity.idelays) + 1
        LOG.info("horizon is %d steps" % self.horizon)

        # workspace -- minimal state of network with delays
        self.good_history_shape = (self.horizon, self.model.nvar,
                                   self.number_of_nodes,
                                   self.model.number_of_modes)
        msg = "%s: History shape will be: %s"
        LOG.debug(msg % (repr(self), str(self.good_history_shape)))

        #Reshape integrator.noise.nsig, if necessary.
        if isinstance(self.integrator,
                      integrators_module.IntegratorStochastic):
            self.configure_integrator_noise()

        self.configure_history(self.initial_conditions)

        #Configure Monitors to work with selected Model, etc...
        self.configure_monitors()

        #Estimate of memory usage.
        self._census_memory_requirement()

    def __call__(self, simulation_length=None, random_state=None):
        """
        When a Simulator is called it returns an iterator.

        kwargs:

        ``simulation_length``:
           total time of simulation

        ``random_state``: 
           a state for the NumPy random number generator, saved from a previous 
           call to permit consistent continuation of a simulation.

        """
        #The number of times this Simulator has been called.
        self.calls += 1

        #Update the simulator objects simulation_length attribute,
        if simulation_length is None:
            simulation_length = self.simulation_length
        else:
            self.simulation_length = simulation_length

        #Estimate run time and storage requirements, with logging.
        self._guesstimate_runtime()
        self._calculate_storage_requirement()

        if random_state is not None:
            if isinstance(self.integrator,
                          integrators_module.IntegratorStochastic):
                self.integrator.noise.random_stream.set_state(random_state)
                msg = "%s: random_state supplied. Seed is: %s"
                LOG.info(
                    msg %
                    (str(self),
                     str(self.integrator.noise.random_stream.get_state()[1][0])
                     ))
            else:
                msg = "%s: random_state supplied for non-stochastic integration"
                LOG.warn(msg % str(self))

        #Determine the number of integration steps required to produce
        #data of simulation_length
        int_steps = int(simulation_length / self.integrator.dt)
        LOG.info("%s: gonna do %d integration steps" % (str(self), int_steps))

        # locals for cleaner code.
        horizon = self.horizon
        history = self.history
        dfun = self.model.dfun
        coupling = self.coupling
        scheme = self.integrator.scheme
        npsum = numpy.sum
        npdot = numpy.dot
        ncvar = len(self.model.cvar)
        number_of_regions = self.connectivity.number_of_regions
        nsn = (number_of_regions, 1, number_of_regions)

        # Exact dtypes and alignment are required by c speedups. Once we have history objects these will be encapsulated
        # cvar index array broadcastable to nodes, cvars, nodes
        cvar = numpy.array(self.model.cvar[numpy.newaxis, :, numpy.newaxis],
                           dtype=numpy.intc)
        LOG.debug("%s: cvar is: %s" % (str(self), str(cvar)))
        # idelays array broadcastable to nodes, cvars, nodes
        idelays = numpy.array(self.connectivity.idelays[:, numpy.newaxis, :],
                              dtype=numpy.intc,
                              order='c')
        LOG.debug("%s: idelays shape is: %s" % (str(self), str(idelays.shape)))
        # weights array broadcastable to nodes, cva, nodes, modes
        weights = self.connectivity.weights[:, numpy.newaxis, :, numpy.newaxis]
        LOG.debug("%s: weights shape is: %s" % (str(self), str(weights.shape)))
        # node_ids broadcastable to nodes, cvars, nodes
        node_ids = numpy.array(
            numpy.arange(number_of_regions)[numpy.newaxis, numpy.newaxis, :],
            dtype=numpy.intc)
        LOG.debug("%s: node_ids shape is: %s" %
                  (str(self), str(node_ids.shape)))

        if self.surface is None:
            local_coupling = 0.0
        else:
            region_average = self.surface.region_average
            region_history = npdot(
                region_average, history
            )  # this may be very expensive ~60sec for epileptor (many states and modes ...)
            region_history = region_history.transpose((1, 2, 0, 3))
            region_history = numpy.ascontiguousarray(
                region_history)  # required by the c speedups
            if self.surface.coupling_strength.size == 1:
                local_coupling = (self.surface.coupling_strength[0] *
                                  self.surface.local_connectivity.matrix)
            elif self.surface.coupling_strength.size == self.surface.number_of_vertices:
                ind = numpy.arange(self.number_of_nodes, dtype=int)
                vec_cs = numpy.zeros((self.number_of_nodes, ))
                vec_cs[:self.surface.
                       number_of_vertices] = self.surface.coupling_strength
                sp_cs = sparse.csc_matrix(
                    (vec_cs, (ind, ind)),
                    shape=(self.number_of_nodes, self.number_of_nodes))
                local_coupling = sp_cs * self.surface.local_connectivity.matrix

        if self.stimulus is None:
            stimulus = 0.0
        else:  # TODO: Consider changing to simulator absolute time... This is an open discussion, a matter of interpretation of the stimuli time axis.
            time = numpy.arange(0, simulation_length, self.integrator.dt)
            time = time[numpy.newaxis, :]
            self.stimulus.configure_time(time)
            stimulus = numpy.zeros((self.model.nvar, self.number_of_nodes, 1))
            LOG.debug("%s: stimulus shape is: %s" %
                      (str(self), str(stimulus.shape)))

        # initial state, history[timepoint[0], state_variables, nodes, modes]
        state = history[self.current_step % horizon, :]
        LOG.debug("%s: state shape is: %s" % (str(self), str(state.shape)))

        if self.surface is not None:
            # the vertex mapping array is huge but sparse.
            # csr because I expect the row to have one value and I expect the dot to proceed row wise.
            vertex_mapping = sparse.csr_matrix(self.surface.vertex_mapping)
            # this is big a well. same shape as the vertex mapping.
            region_average = sparse.csr_matrix(region_average)

            node_coupling_shape = (vertex_mapping.shape[0], ncvar,
                                   self.model.number_of_modes)

        delayed_state = numpy.zeros(
            (number_of_regions, ncvar, number_of_regions,
             self.model.number_of_modes))

        for step in xrange(self.current_step + 1,
                           self.current_step + int_steps + 1):
            time_indices = (step - 1 - idelays) % horizon
            if self.surface is None:
                get_state(history,
                          time_indices,
                          cvar,
                          node_ids,
                          out=delayed_state)
                node_coupling = coupling(weights, state[self.model.cvar],
                                         delayed_state)
            else:
                get_state(region_history,
                          time_indices,
                          cvar,
                          node_ids,
                          out=delayed_state)
                region_coupling = coupling(
                    weights, region_history[(step - 1) % horizon,
                                            self.model.cvar], delayed_state)
                node_coupling = numpy.empty(node_coupling_shape)

                # sparse matrices cannot multiply with 3d arrays so we use a loop over the modes
                for mi in xrange(self.model.number_of_modes):
                    node_coupling[...,
                                  mi] = vertex_mapping * region_coupling[...,
                                                                         mi].T

                node_coupling = node_coupling.transpose((1, 0, 2))

            if self.stimulus is not None:
                stimulus[self.model.cvar, :, :] = numpy.reshape(
                    self.stimulus(step - (self.current_step + 1)), (1, -1, 1))

            state = scheme(state, dfun, node_coupling, local_coupling,
                           stimulus)
            history[step % horizon, :] = state

            if self.surface is not None:
                # this optimisation is similar to the one done for vertex_mapping above
                step_avg = numpy.empty((number_of_regions, state.shape[0],
                                        self.model.number_of_modes))
                for mi in xrange(self.model.number_of_modes):
                    step_avg[..., mi] = region_average.dot(state[..., mi].T)

                region_history[step % horizon, :] = step_avg.transpose(
                    (1, 0, 2))

            # monitor.things e.g. raw, average, eeg, meg, fmri...
            output = [monitor.record(step, state) for monitor in self.monitors]
            if any(outputi is not None for outputi in output):
                yield output

        # This -1 is here for not repeating the point on resume
        self.current_step = self.current_step + int_steps - 1
        self.history = history

    def configure_history(self, initial_conditions=None):
        """
        Set initial conditions for the simulation using either the provided 
        initial_conditions or, if none are provided, the model's initial() 
        method. This method is called durin the Simulator's __init__(). 

        Any initial_conditions that are provided as an argument are expected 
        to have dimensions 1, 2, and 3 with shapse corresponding to the number
        of state_variables, nodes and modes, respectively. If the provided 
        inital_conditions are shorter in time (dim=0) than the required history
        the model's initial() method is called to make up the difference.

        """

        history = self.history
        if initial_conditions is None:
            msg = "%s: Setting default history using model's initial() method."
            LOG.info(msg % str(self))
            history = self.model.initial(self.integrator.dt,
                                         self.good_history_shape)
        else:
            # history should be [timepoints, state_variables, nodes, modes]
            LOG.info("%s: Received initial conditions as arg." % str(self))
            ic_shape = initial_conditions.shape
            if ic_shape[1:] != self.good_history_shape[1:]:
                msg = "%s: bad initial_conditions[1:] shape %s, should be %s"
                msg %= self, ic_shape[1:], self.good_history_shape[1:]
                raise ValueError(msg)
            else:
                if ic_shape[0] >= self.horizon:
                    msg = "%s: Using last %s time-steps for history."
                    LOG.info(msg % (str(self), self.horizon))
                    history = initial_conditions[
                        -self.horizon:, :, :, :].copy()
                else:
                    msg = "%s: initial_conditions shorter than required."
                    LOG.info(msg % str(self))
                    msg = "%s: Using model's initial() method for difference."
                    LOG.info(msg % str(self))
                    history = self.model.initial(self.integrator.dt,
                                                 self.good_history_shape)
                    csmh = self.current_step % self.horizon
                    history = numpy.roll(history, -csmh, axis=0)
                    history[:ic_shape[0], :, :, :] = initial_conditions
                    history = numpy.roll(history, csmh, axis=0)
                self.current_step += ic_shape[0] - 1
            msg = "%s: history shape is: %s"
            LOG.debug(msg % (str(self), str(history.shape)))
        self.history = history

    def configure_integrator_noise(self):
        """
        This enables having noise to be state variable specific and/or to enter 
        only via specific brain structures, for example it we only want to 
        consider noise as an external input entering the brain via appropriate
        thalamic nuclei.

        Support 3 possible shapes:
            1) number_of_nodes;

            2) number_of_state_variables; and 

            3) (number_of_state_variables, number_of_nodes).

        """

        noise = self.integrator.noise

        if self.integrator.noise.ntau > 0.0:
            self.integrator.noise.configure_coloured(
                self.integrator.dt, self.good_history_shape[1:])
        else:
            self.integrator.noise.configure_white(self.integrator.dt,
                                                  self.good_history_shape[1:])

        if self.surface is not None:
            if self.integrator.noise.nsig.size == self.connectivity.number_of_regions:
                self.integrator.noise.nsig = self.integrator.noise.nsig[
                    self.surface.region_mapping]
            elif self.integrator.noise.nsig.size == self.model.nvar * self.connectivity.number_of_regions:
                self.integrator.noise.nsig = self.integrator.noise.nsig[:,
                                                                        self.
                                                                        surface
                                                                        .
                                                                        region_mapping]

        good_nsig_shape = (self.model.nvar, self.number_of_nodes,
                           self.model.number_of_modes)
        nsig = self.integrator.noise.nsig
        LOG.debug("Simulator.integrator.noise.nsig shape: %s" %
                  str(nsig.shape))
        if nsig.shape in (good_nsig_shape, (1, )):
            return
        elif nsig.shape == (self.model.nvar, ):
            nsig = nsig.reshape((self.model.nvar, 1, 1))
        elif nsig.shape == (self.number_of_nodes, ):
            nsig = nsig.reshape((1, self.number_of_nodes, 1))
        elif nsig.shape == (self.model.nvar, self.number_of_nodes):
            nsig = nsig.reshape((self.model.nvar, self.number_of_nodes, 1))
        else:
            msg = "Bad Simulator.integrator.noise.nsig shape: %s"
            LOG.error(msg % str(nsig.shape))

        LOG.debug("Simulator.integrator.noise.nsig shape: %s" %
                  str(nsig.shape))
        self.integrator.noise.nsig = nsig

    def configure_monitors(self):
        """ Configure the requested Monitors for this Simulator """
        if not isinstance(self.monitors, (list, tuple)):
            self.monitors = [self.monitors]

        # Configure monitors
        for monitor in self.monitors:
            monitor.config_for_sim(self)

    def configure_stimuli(self):
        """ Configure the defined Stimuli for this Simulator """
        if self.stimulus is not None:
            if self.surface:
                self.stimulus.configure_space(self.surface.region_mapping)
            else:
                self.stimulus.configure_space()

    def memory_requirement(self):
        """
        Return an estimated of the memory requirements (Bytes) for this
        simulator's current configuration.
        """
        self._guesstimate_memory_requirement()
        return self._memory_requirement_guess

    def runtime(self, simulation_length):
        """
        Return an estimated run time (seconds) for the simulator's current 
        configuration and a specified simulation length.

        """
        self.simulation_length = simulation_length
        self._guesstimate_runtime()
        return self._runtime

    def storage_requirement(self, simulation_length):
        """
        Return an estimated storage requirement (Bytes) for the simulator's
        current configuration and a specified simulation length.

        """
        self.simulation_length = simulation_length
        self._calculate_storage_requirement()
        return self._storage_requirement

    def _guesstimate_memory_requirement(self):
        """
        guesstimate the memory required for this simulator.

        Guesstimate is based on the shape of the dominant arrays, and as such 
        can operate before configuration.

        NOTE: Assumes returned/yeilded data is in some sense "taken care of" in
            the world outside the simulator, and so doesn't consider it, making
            the simulator's history, and surface if present, the dominant 
            memory pigs...

        """
        if self.surface:
            number_of_nodes = self.surface.number_of_vertices
        else:
            number_of_nodes = self.connectivity.number_of_regions

        number_of_regions = self.connectivity.number_of_regions

        magic_number = 2.42  # Current guesstimate is low by about a factor of 2, seems safer to over estimate...
        bits_64 = 8.0  # Bytes
        bits_32 = 4.0  # Bytes
        #NOTE: The speed hack for getting the first element of hist shape should
        #      partially resolves calling of this method with a non-configured
        #     connectivity, there remains the less common issue if no tract_lengths...
        hist_shape = (
            self.connectivity.tract_lengths.max() /
            (self.conduction_speed or self.connectivity.speed or 3.0) /
            self.integrator.dt, self.model.nvar, number_of_nodes,
            self.model.number_of_modes)
        memreq = numpy.prod(hist_shape) * bits_64
        if self.surface:
            memreq += self.surface.number_of_triangles * 3 * bits_32 * 2  # normals
            memreq += self.surface.number_of_vertices * 3 * bits_64 * 2  # normals
            memreq += number_of_nodes * number_of_regions * bits_64 * 4  # vertex_mapping, region_average, region_sum
            #???memreq += self.surface.local_connectivity.matrix.nnz * 8

        if not isinstance(self.monitors, (list, tuple)):
            monitors = [self.monitors]
        else:
            monitors = self.monitors
        for monitor in monitors:
            if not isinstance(monitor, monitors_module.Bold):
                stock_shape = (monitor.period / self.integrator.dt,
                               self.model.variables_of_interest.shape[0],
                               number_of_nodes, self.model.number_of_modes)
                memreq += numpy.prod(stock_shape) * bits_64
                if hasattr(monitor, "sensors"):
                    try:
                        memreq += number_of_nodes * monitor.sensors.number_of_sensors * bits_64  # projection_matrix
                    except AttributeError:
                        LOG.debug(
                            "No sensors specified, guessing memory based on default EEG."
                        )
                        memreq += number_of_nodes * 62.0 * bits_64

            else:
                stock_shape = (monitor.hrf_length * monitor._stock_sample_rate,
                               self.model.variables_of_interest.shape[0],
                               number_of_nodes, self.model.number_of_modes)
                interim_stock_shape = (
                    1.0 / (2.0**-2 * self.integrator.dt),
                    self.model.variables_of_interest.shape[0], number_of_nodes,
                    self.model.number_of_modes)
                memreq += numpy.prod(stock_shape) * bits_64
                memreq += numpy.prod(interim_stock_shape) * bits_64

        if psutil and memreq > psutil.virtual_memory().total:
            LOG.error("This is gonna get ugly...")

        self._memory_requirement_guess = magic_number * memreq
        msg = "Memory requirement guesstimate: simulation will need about %.1f MB"
        LOG.info(msg % (self._memory_requirement_guess / 1048576.0))

    def _census_memory_requirement(self):
        """
        Guesstimate the memory required for this simulator. 

        Guesstimate is based on a census of the dominant arrays after the
        simulator has been configured.

        NOTE: Assumes returned/yeilded data is in some sense "taken care of" in
            the world outside the simulator, and so doesn't consider it, making
            the simulator's history, and surface if present, the dominant 
            memory pigs...

        """
        magic_number = 2.42  # Current guesstimate is low by about a factor of 2, seems safer to over estimate...
        memreq = self.history.nbytes
        try:
            memreq += self.surface.triangles.nbytes * 2
            memreq += self.surface.vertices.nbytes * 2
            memreq += self.surface.vertex_mapping.nbytes * 4  # vertex_mapping, region_average, region_sum
            memreq += self.surface.eeg_projection.nbytes
            memreq += self.surface.local_connectivity.matrix.nnz * 8
        except AttributeError:
            pass

        for monitor in self.monitors:
            memreq += monitor._stock.nbytes
            if isinstance(monitor, monitors_module.Bold):
                memreq += monitor._interim_stock.nbytes

        if psutil and memreq > psutil.virtual_memory().total:
            LOG.error("This is gonna get ugly...")

        self._memory_requirement_census = magic_number * memreq
        #import pdb; pdb.set_trace()
        msg = "Memory requirement census: simulation will need about %.1f MB"
        LOG.info(msg % (self._memory_requirement_census / 1048576.0))

    def _guesstimate_runtime(self):
        """
        Estimate the runtime for this simulator.

        Spread in parallel executions of larger arrays means this will be an over-estimation,
        or rather a single threaded estimation...
        Different choice of integrators and monitors has an additional effect,
        on the magic number though relatively minor

        """
        magic_number = 6.57e-06  # seconds
        self._runtime = (magic_number * self.number_of_nodes *
                         self.model.nvar * self.model.number_of_modes *
                         self.simulation_length / self.integrator.dt)
        msg = "Simulation single-threaded runtime should be about %s seconds!"
        LOG.info(msg % str(int(self._runtime)))

    def _calculate_storage_requirement(self):
        """
        Calculate the storage requirement for the simulator, configured with
        models, monitors, etc being run for a particular simulation length. 
        While this is only approximate, it is far more reliable/accurate than
        the memory and runtime guesstimates.
        """
        LOG.info("Calculating storage requirement for ...")
        strgreq = 0
        for monitor in self.monitors:
            # Avoid division by zero for monitor not yet configured
            # (in framework this is executed, when only preconfigure has been called):
            current_period = monitor.period or self.integrator.dt
            strgreq += (TvbProfile.current.MAGIC_NUMBER *
                        self.simulation_length * self.number_of_nodes *
                        self.model.nvar * self.model.number_of_modes /
                        current_period)
        LOG.info("Calculated storage requirement for simulation: %d " %
                 int(strgreq))
        self._storage_requirement = int(strgreq)
sim = simulator.Simulator(
        model        = models.Generic2dOscillator(),
        connectivity = connectivity.Connectivity(speed=4.0, load_default=True),
        coupling     = coupling.Linear(a=-2 ** -9),
        integrator   = integrators.HeunStochastic(
                            dt=2 ** -4,
                            noise=noise.Additive(nsig=ones((2,)) * 0.001)
                            ),
        monitors     = (
            monitors.EEG(period=1e3/2 ** 10), # 1024 Hz
            monitors.Bold(period=500)       # 0.5  Hz
            ),
        surface      = Cortex(
            load_default=True,
            local_connectivity = lconn,
            coupling_strength  = array([0.01])
            ),
        )

sim.configure()

# set delays to mean
print sim.connectivity.idelays
sim.connectivity.delays[:] = sim.connectivity.delays.mean()
sim.connectivity.set_idelays(sim.integrator.dt)
print sim.connectivity.idelays

ts_eeg, ys_eeg = [], []
ts_bold, ys_bold = [], []
#
#

"""

.. moduleauthor:: Stuart A. Knock <*****@*****.**>

"""
from tvb.datatypes.cortex import Cortex

from tvb.simulator.lab import *
from tvb.simulator.region_boundaries import RegionBoundaries
from tvb.simulator.region_colours import RegionColours 


CORTEX = Cortex.from_file()
CORTEX_BOUNDARIES = RegionBoundaries(CORTEX)

region_colours = RegionColours(CORTEX_BOUNDARIES.region_neighbours)
colouring = region_colours.back_track()

#Make the hemispheres symmetric
# TODO: should prob. et colouring for one hemisphere then just stack two copies...
number_of_regions = len(CORTEX_BOUNDARIES.region_neighbours)
for k in range(int(number_of_regions)):
    colouring[k + int(number_of_regions)] = colouring[k]


mapping_colours = list("rgbcmyRGBCMY")
colour_rgb = {"r": numpy.array([255,   0,   0], dtype=numpy.uint8),
              "g": numpy.array([  0, 255,   0], dtype=numpy.uint8),
예제 #23
0
    def configure(self,
                  model,
                  model_parameters,
                  integrator,
                  integrator_parameters,
                  connectivity,
                  monitors,
                  monitors_parameters=None,
                  surface=None,
                  surface_parameters=None,
                  stimulus=None,
                  coupling=None,
                  coupling_parameters=None,
                  initial_conditions=None,
                  conduction_speed=None,
                  simulation_length=0,
                  simulation_state=None):
        """
        Make preparations for the adapter launch.
        """
        self.log.debug("available_couplings: %s..." %
                       str(self.available_couplings))
        self.log.debug("coupling: %s..." % str(coupling))
        self.log.debug("coupling_parameters: %s..." % str(coupling_parameters))

        self.log.debug("%s: Initializing Model..." % str(self))
        noise_framework.build_noise(model_parameters)
        model_instance = self.available_models[str(model)](**model_parameters)
        self._validate_model_parameters(model_instance, connectivity, surface)

        self.log.debug("%s: Initializing Integration scheme..." % str(self))
        noise_framework.build_noise(integrator_parameters)
        integr = self.available_integrators[integrator](
            **integrator_parameters)

        self.log.debug("%s: Instantiating Monitors..." % str(self))
        monitors_list = []
        for monitor_name in monitors:
            if (monitors_parameters is not None) and (str(monitor_name)
                                                      in monitors_parameters):
                current_monitor_parameters = monitors_parameters[str(
                    monitor_name)]
                HRFKernelEquation.build_equation_from_dict(
                    'hrf_kernel', current_monitor_parameters, True)
                monitors_list.append(
                    self.available_monitors[str(monitor_name)](
                        **current_monitor_parameters))
            else:
                ### We have monitors without any UI settable parameter.
                monitors_list.append(
                    self.available_monitors[str(monitor_name)]())

        if len(monitors) < 1:
            raise LaunchException(
                "Can not launch operation without monitors selected !!!")

        self.log.debug("%s: Initializing Coupling..." % str(self))
        coupling_inst = self.available_couplings[str(coupling)](
            **coupling_parameters)

        self.log.debug("Initializing Cortex...")
        if self._is_surface_simulation(surface, surface_parameters):
            cortex_entity = Cortex(use_storage=False).populate_cortex(
                surface, surface_parameters)
            if cortex_entity.region_mapping_data.connectivity.number_of_regions != connectivity.number_of_regions:
                raise LaunchException(
                    "Incompatible RegionMapping -- Connectivity !!")
            if cortex_entity.region_mapping_data.surface.number_of_vertices != surface.number_of_vertices:
                raise LaunchException(
                    "Incompatible RegionMapping -- Surface !!")
            select_loc_conn = cortex_entity.local_connectivity
            if select_loc_conn is not None and select_loc_conn.surface.number_of_vertices != surface.number_of_vertices:
                raise LaunchException(
                    "Incompatible LocalConnectivity -- Surface !!")
        else:
            cortex_entity = None

        self.log.debug("%s: Instantiating requested simulator..." % str(self))

        if conduction_speed not in (0.0, None):
            connectivity.speed = numpy.array([conduction_speed])
        else:
            raise LaunchException("conduction speed cannot be 0 or missing")

        self.algorithm = Simulator(connectivity=connectivity,
                                   coupling=coupling_inst,
                                   surface=cortex_entity,
                                   stimulus=stimulus,
                                   model=model_instance,
                                   integrator=integr,
                                   monitors=monitors_list,
                                   initial_conditions=initial_conditions,
                                   conduction_speed=conduction_speed)
        self.simulation_length = simulation_length
        self.log.debug("%s: Initializing storage..." % str(self))
        try:
            self.algorithm.preconfigure()
        except ValueError as err:
            raise LaunchException(
                "Failed to configure simulator due to invalid Input Values. It could be because "
                "of an incompatibility between different version of TVB code.",
                err)
white_matter_coupling = coupling.Linear(a=0.0043)   # 0.0066

#Initialise an Integrator
hiss = noise.Additive(nsig=numpy.array([2 ** -16, ]))
heunint = integrators.HeunStochastic(dt=2 ** -4, noise=hiss)

#Initialise some Monitors with period in physical time
mon_tavg = monitors.TemporalAverage(period=2 ** -2)
mon_savg = monitors.SpatialAverage(period=2 ** -2)
mon_eeg = monitors.EEG(period=2 ** -2)

#Bundle them
what_to_watch = (mon_tavg, mon_savg, mon_eeg)

#Initialise a surface fully loaded
default_cortex = Cortex(load_default=True)

#Initialise Simulator -- Model, Connectivity, Integrator, Monitors, and surface.
sim = simulator.Simulator(model=rfhn, connectivity=white_matter,
                          coupling=white_matter_coupling,
                          integrator=heunint, monitors=what_to_watch,
                          surface=default_cortex)

sim.configure()

#Clear initial transient
LOG.info("Initial run to clear transient...")
for _, _, _ in sim(simulation_length=2 ** 6):
    pass
LOG.info("Finished initial run to clear transient.")
예제 #25
0
mon_eeg = monitors.EEG(period=2**-2)

#Bundle them
what_to_watch = (mon_tavg, mon_savg, mon_eeg)

#Initialise a surface:
#First define the function describing the "local" connectivity.
grey_matter = LocalConnectivity(cutoff=40.0)
grey_matter.equation.parameters['sigma'] = 10.0
grey_matter.equation.parameters['amp'] = 1.0

#then a scaling factor, to adjust the strength of the local connectivity
local_coupling_strength = numpy.array([-0.0115])

#finally, create a default cortex that includes the custom local connectivity.
default_cortex = Cortex(load_default=True)
default_cortex.local_connectivity = grey_matter
default_cortex.coupling_strength = local_coupling_strength

#Initialise Simulator -- Model, Connectivity, Integrator, Monitors, and surface.
sim = simulator.Simulator(model=oscillator,
                          connectivity=white_matter,
                          integrator=heunint,
                          monitors=what_to_watch,
                          surface=default_cortex)
sim.configure()

LOG.info("Starting simulation...")
#Perform the simulation
tavg_data = []
tavg_time = []
white_matter_coupling = coupling.Linear(a=2 ** -7)

#Initialise an Integrator
heunint = integrators.HeunDeterministic(dt=2 ** -4)

#Initialise some Monitors with period in physical time
mon_tavg = monitors.TemporalAverage(period=2 ** -2)
mon_savg = monitors.SpatialAverage(period=2 ** -2)
mon_eeg = monitors.EEG(period=2 ** -2)

#Bundle them
what_to_watch = (mon_tavg, mon_savg, mon_eeg)

#Initialise a surface
local_coupling_strength = numpy.array([2 ** -6])
default_cortex = Cortex(load_default=True)
default_cortex.coupling_strength = local_coupling_strength

##NOTE: THIS IS AN EXAMPLE OF DESCRIBING A SURFACE STIMULUS AT REGIONS LEVEL. 
#       SURFACES ALSO SUPPORT STIMULUS SPECIFICATION BY A SPATIAL FUNCTION 
#       CENTRED AT A VERTEX (OR VERTICES).
#Define the stimulus
#Specify a weighting for regions to receive stimuli...
white_matter.configure()    # Because we want access to number_of_regions
nodes = [0, 7, 13, 33, 42]
#NOTE: here, we specify space at region level simulator will map to surface 
#Specify a weighting for regions to receive stimuli... 
weighting = numpy.zeros((white_matter.number_of_regions, 1))
weighting[nodes] = numpy.array([2.0 ** -2, 2.0 ** -3, 2.0 ** -4, 2.0 ** -5, 2.0 ** -6])[:, numpy.newaxis]

eqn_t = equations.Gaussian()
예제 #27
0
class ProjectionMatrix(core.Type):
    """
    Provides the mechanisms necessary to access OpenMEEG for the calculation of
    EEG and MEG projection matrices, ie matrices that map source activity to 
    sensor activity. It is initialised with datatypes of TVB and ultimately 
    returns the projection matrix as a Numpy ndarray. 
    """

    brain_skull = surfaces_module.BrainSkull(
        label = "Boundary between skull and skin domains",
        default = None,
        required = True,
        doc = """A ... surface on which ... including ...""")

    skull_skin = surfaces_module.SkullSkin(
        label = "surface and auxillary for surface sim",
        default = None,
        required = True,
        doc = """A ... surface on which ... including ...""")

    skin_air = surfaces_module.SkinAir(
        label = "surface and auxillary for surface sim",
        default = None,
        required = True,
        doc = """A ... surface on which ... including ...""")

    conductances = basic.Dict(
        label = "Domain conductances",
        default = {'air': 0.0, 'skin': 1.0, 'skull': 0.01, 'brain': 1.0},
        required = True,
        doc = """A dictionary representing the conductances of ...""")

    sources = Cortex(
        label = "surface and auxillary for surface sim",
        default = None,
        required = True,
        doc = """A cortical surface on which ... including ...""")

    sensors = sensors_module.Sensors(
        label = "surface and auxillary for surface sim",
        default = None,
        required = False,
        doc = """A cortical surface on which ... including ...  
            If left as None then EEG is assumed and skin_air is expected to 
            already has sensors associated""")


    def __init__(self, **kwargs):
        """
        Initialse traited attributes and attributes that will hold OpenMEEG 
        objects.
        """
        super(ProjectionMatrix, self).__init__(**kwargs) 
        LOG.debug(str(kwargs))

        #OpenMEEG attributes
        self.om_head = None
        self.om_sources = None
        self.om_sensors = None
        self.om_head2sensor = None

        self.om_inverse_head = None
        self.om_source_matrix = None
        self.om_source2sensor = None #For MEG, not used for EEG


    def configure(self):
        """
        Converts TVB objects into a for accessible to OpenMEEG, then uses the 
        OpenMEEG library to calculate the intermediate matrices needed in 
        obtaining the final projection matrix.
        """
        super(ProjectionMatrix, self).configure()
        if self.sensors is None:
            self.sensors = self.skin_air.sensors

        if isinstance(self.sensors, sensors_module.SensorsEEG):
            self.skin_air.sensors = self.sensors
            self.skin_air.sensor_locations = self.sensors.sensors_to_surface(self.skin_air)

        # Create OpenMEEG objects from TVB objects.
        self.om_head = self.create_om_head()
        self.om_sources = self.create_om_sources()
        self.om_sensors = self.create_om_sensors()

        # Calculate based on type of sources
        if isinstance(self.sources, Cortex):
            self.om_source_matrix = self.surface_source() #NOTE: ~1 hr
        elif isinstance(self.sources, connectivity_module.Connectivity):
            self.om_source_matrix = self.dipole_source()

        # Calculate based on type of sensors
        if isinstance(self.sensors, sensors_module.SensorsEEG):
            self.om_head2sensor = self.head2eeg()
        elif isinstance(self.sensors, sensors_module.SensorsMEG):
            self.om_head2sensor = self.head2meg()
            if isinstance(self.sources, Cortex):
                self.om_source2sensor = self.surf2meg()
            elif isinstance(self.sources, connectivity_module.Connectivity):
                self.om_source2sensor = self.dip2meg()

        #NOTE: ~1 hr
        self.om_inverse_head = self.inverse_head(inv_head_mat_file = "hminv_uid")


    def __call__(self):
        """
        Having configured the ProjectionMatrix instance, that is having run the 
        configure() method or otherwise provided the intermedite OpenMEEG (om_*)
        attributes, the oblect can be called as a function -- returning a 
        projection matrix as a Numpy array.
        """
        #Check source type and sensor type, then call appripriate methods to 
        #generate intermediate data, cascading all the way back to geometry 
        #calculation if it wasn't already done.
        #Then return a projection matrix...

        # NOTE: returned projection_matrix is a numpy.ndarray
        if isinstance(self.sensors, sensors_module.SensorsEEG):
            projection_matrix = self.eeg_gain()
        elif isinstance(self.sensors, sensors_module.SensorsMEG):
            projection_matrix = self.meg_gain()

        return projection_matrix


    ##------------------------------------------------------------------------##
    ##--------------- Methods for creating openmeeg objects ------------------##
    ##------------------------------------------------------------------------##

    def create_om_head(self): #TODO: Prob. need to make file names specifiable
        """
        Generates 5 files::

            skull_skin.tri
            skin_air.tri
            brain_skull.tri
            head_model.geom
            head_model.cond

        Containing the specification of a head in a form that can be read by 
        OpenMEEG, then creates and returns an OpenMEEG Geometry object containing 
        this information.
        """
        surface_files = []
        surface_files.append(self._tvb_surface_to_tri("skull_skin.tri"))
        surface_files.append(self._tvb_surface_to_tri("brain_skull.tri"))
        surface_files.append(self._tvb_surface_to_tri("skin_air.tri"))

        geometry_file = self._write_head_geometry(surface_files,
                                                  "head_model.geom")
        conductances_file = self._write_conductances("head_model.cond")

        LOG.info("Creating OpenMEEG Geometry object for the head...")
        om_head = om.Geometry()
        om_head.read(geometry_file, conductances_file)
        #om_head.selfCheck() #Didn't catch bad order...
        LOG.info("OpenMEEG Geometry object for the head successfully created.")
        return om_head


    def create_om_sources(self): #TODO: Prob. should make file names specifiable
        """
        Take a TVB Connectivity or Cortex object and return an OpenMEEG object
        that specifies sources, a Matrix object for region level sources or a
        Mesh object for a cortical surface source.
        """
        if isinstance(self.sources, connectivity_module.Connectivity):
            sources_file = self._tvb_connectivity_to_txt("sources.txt")
            om_sources = om.Matrix()
        elif isinstance(self.sources, Cortex):
            sources_file = self._tvb_surface_to_tri("sources.tri")
            om_sources = om.Mesh()
        else:
            LOG.error("sources must be either a Connectivity or Cortex.")

        om_sources.load(sources_file)
        return om_sources


    def create_om_sensors(self, file_name=None):
        """
        Take a TVB Sensors object and return an OpenMEEG Sensors object.
        """
        if isinstance(self.sensors, sensors_module.SensorsEEG):
            file_name = file_name or "eeg_sensors.txt"
            sensors_file = self._tvb_eeg_sensors_to_txt(file_name)
        elif isinstance(self.sensors, sensors_module.SensorsMEG):
            file_name = file_name or "meg_sensors.squid"
            sensors_file = self._tvb_meg_sensors_to_squid(file_name)
        else:
            LOG.error("sensors should be either SensorsEEG or SensorsMEG")

        LOG.info("Wrote sensors to temporary file: %s" % str(file_name))

        om_sensors = om.Sensors()
        om_sensors.load(sensors_file)
        return om_sensors



    ##------------------------------------------------------------------------##
    ##--------- Methods for calling openmeeg methods, with logging. ----------##
    ##------------------------------------------------------------------------##

    def surf2meg(self):
        """
        Create a matrix that can be used to map an OpenMEEG surface source to an 
        OpenMEEG MEG Sensors object.

        NOTE: This source to sensor mapping is not required for EEG.

        """
        LOG.info("Computing DipSource2MEGMat...")
        surf2meg_mat = om.SurfSource2MEGMat(self.om_sources, self.om_sensors)
        LOG.info("surf2meg: %d x %d" % (surf2meg_mat.nlin(),
                                        surf2meg_mat.ncol()))
        return surf2meg_mat


    def dip2meg(self):
        """
        Create an OpenMEEG Matrix that can be used to map OpenMEEG dipole sources 
        to an OpenMEEG MEG Sensors object.

        NOTE: This source to sensor mapping is not required for EEG.

        """
        LOG.info("Computing DipSource2MEGMat...")
        dip2meg_mat = om.DipSource2MEGMat(self.om_sources, self.om_sensors)
        LOG.info("dip2meg: %d x %d" % (dip2meg_mat.nlin(), dip2meg_mat.ncol()))
        return dip2meg_mat


    def head2eeg(self):
        """
        Call OpenMEEG's Head2EEGMat method to calculate the head to EEG sensor
        matrix.
        """     
        LOG.info("Computing Head2EEGMat...")
        h2s_mat = om.Head2EEGMat(self.om_head, self.om_sensors)
        LOG.info("head2eeg: %d x %d" % (h2s_mat.nlin(), h2s_mat.ncol()))
        return h2s_mat


    def head2meg(self):
        """
        Call OpenMEEG's Head2MEGMat method to calculate the head to MEG sensor
        matrix.
        """     
        LOG.info("Computing Head2MEGMat...")
        h2s_mat = om.Head2MEGMat(self.om_head, self.om_sensors)
        LOG.info("head2meg: %d x %d" % (h2s_mat.nlin(), h2s_mat.ncol()))
        return h2s_mat


    def surface_source(self, gauss_order = 3, surf_source_file=None):
        """ 
        Call OpenMEEG's SurfSourceMat method to calculate a surface source 
        matrix. Optionaly saving the matrix for later use.
        """
        LOG.info("Computing SurfSourceMat...")
        ssm = om.SurfSourceMat(self.om_head, self.om_sources, gauss_order)
        LOG.info("surface_source_mat: %d x %d" % (ssm.nlin(), ssm.ncol()))
        if surf_source_file is not None:
            LOG.info("Saving surface_source matrix as %s..." % surf_source_file)
            ssm.save(os.path.join(OM_STORAGE_DIR,
                                  surf_source_file + OM_SAVE_SUFFIX)) #~3GB
        return ssm


    def dipole_source(self, gauss_order = 3, use_adaptive_integration = True,
                      dip_source_file=None):
        """ 
        Call OpenMEEG's DipSourceMat method to calculate a dipole source matrix.
        Optionaly saving the matrix for later use.
        """
        LOG.info("Computing DipSourceMat...")
        dsm   = om.DipSourceMat(self.om_head, self.om_sources, gauss_order, 
                                use_adaptive_integration)
        LOG.info("dipole_source_mat: %d x %d" % (dsm.nlin(), dsm.ncol()))
        if dip_source_file is not None:
            LOG.info("Saving dipole_source matrix as %s..." % dip_source_file)
            dsm.save(os.path.join(OM_STORAGE_DIR,
                                  dip_source_file + OM_SAVE_SUFFIX))
        return dsm


    def inverse_head(self, gauss_order = 3, inv_head_mat_file = None):
        """
        Call OpenMEEG's HeadMat method to calculate a head matrix. The inverse 
        method of the head matrix is subsequently called to invert the matrix.
        Optionaly saving the inverted matrix for later use.

        Runtime ~8 hours, mostly in martix inverse as I just use a stock ATLAS 
        install which doesn't appear to be multithreaded (custom building ATLAS
        should sort this)... Under Windows it should use MKL, not sure for Mac

        For reg13+potato surfaces, saved file size: hminv ~ 5GB, ssm ~ 3GB.
        """

        LOG.info("Computing HeadMat...")
        head_matrix = om.HeadMat(self.om_head, gauss_order)
        LOG.info("head_matrix: %d x %d" % (head_matrix.nlin(), 
                                           head_matrix.ncol()))

        LOG.info("Inverting HeadMat...")
        hminv = head_matrix.inverse()
        LOG.info("inverse head_matrix: %d x %d" % (hminv.nlin(), hminv.ncol()))

        if inv_head_mat_file is not None:
            LOG.info("Saving inverse_head matrix as %s..." % inv_head_mat_file)
            hminv.save(os.path.join(OM_STORAGE_DIR,
                                    inv_head_mat_file + OM_SAVE_SUFFIX)) #~5GB
        return hminv


    def eeg_gain(self, eeg_file=None):
        """
        Call OpenMEEG's GainEEG method to calculate the final projection matrix.
        Optionaly saving the matrix for later use. The OpenMEEG matrix is 
        converted to a Numpy array before return. 
        """
        LOG.info("Computing GainEEG...")
        eeg_gain = om.GainEEG(self.om_inverse_head, self.om_source_matrix,
                              self.om_head2sensor)
        LOG.info("eeg_gain: %d x %d" % (eeg_gain.nlin(), eeg_gain.ncol()))
        if eeg_file is not None:
            LOG.info("Saving eeg_gain as %s..." % eeg_file)
            eeg_gain.save(os.path.join(OM_STORAGE_DIR,
                                       eeg_file + OM_SAVE_SUFFIX))
        return om.asarray(eeg_gain)


    def meg_gain(self, meg_file=None):
        """
        Call OpenMEEG's GainMEG method to calculate the final projection matrix.
        Optionaly saving the matrix for later use. The OpenMEEG matrix is 
        converted to a Numpy array before return. 
        """
        LOG.info("Computing GainMEG...")
        meg_gain = om.GainMEG(self.om_inverse_head, self.om_source_matrix,
                              self.om_head2sensor, self.om_source2sensor)
        LOG.info("meg_gain: %d x %d" % (meg_gain.nlin(), meg_gain.ncol()))
        if meg_file is not None:
            LOG.info("Saving meg_gain as %s..." % meg_file)
            meg_gain.save(os.path.join(OM_STORAGE_DIR,
                                       meg_file + OM_SAVE_SUFFIX))
        return om.asarray(meg_gain)



    ##------------------------------------------------------------------------##
    ##------- Methods for writting temporary files loaded by openmeeg --------##
    ##------------------------------------------------------------------------##

    def _tvb_meg_sensors_to_squid(self, sensors_file_name):
        """
        Write a tvb meg_sensor datatype to a .squid file, so that OpenMEEG can
        read it and compute the projection matrix for MEG...
        """
        sensors_file_path = os.path.join(OM_STORAGE_DIR, sensors_file_name)
        meg_sensors = numpy.hstack((self.sensors.locations, 
                                    self.sensors.orientations))
        numpy.savetxt(sensors_file_path, meg_sensors)
        return sensors_file_path


    def _tvb_connectivity_to_txt(self, dipoles_file_name):
        """
        Write position and orientation information from a TVB connectivity object 
        to a text file that can be read as source dipoles by OpenMEEG. 

        NOTE: Region level simulations lack sufficient detail of source orientation, 
            etc, to provide anything but superficial relevance. It's probably better
            to do a mapping of region level simulations to a surface and then
            perform the EEG projection from the mapped data...

        """
        NotImplementedError


    def _tvb_surface_to_tri(self, surface_file_name):
        """
        Write a tvb surface datatype to .tri format, so that OpenMEEG can read
        it and compute projection matrices for EEG/MEG/...
        """
        surface_file_path = os.path.join(OM_STORAGE_DIR, surface_file_name)

        #TODO: check file doesn't already exist
        LOG.info("Writing TVB surface to .tri file: %s" % surface_file_path)
        file_handle = file(surface_file_path, "a")

        file_handle.write("- %d \n" % self.sources.number_of_vertices)
        verts_norms = numpy.hstack((self.sources.vertices, 
                                    self.sources.vertex_normals))
        numpy.savetxt(file_handle, verts_norms)

        tri_str = "- " + (3 * (str(self.sources.number_of_triangles) + " ")) + "\n"
        file_handle.write(tri_str)
        numpy.savetxt(file_handle, self.sources.triangles, fmt="%d")

        file_handle.close()
        LOG.info("%s written successfully." % surface_file_name)

        return surface_file_path


    def _tvb_eeg_sensors_to_txt(self, sensors_file_name):
        """
        Write a tvb eeg_sensor datatype (after mapping to the head surface to be 
        used) to a .txt file, so that OpenMEEG can read it and compute 
        leadfield/projection/forward_solution matrices for EEG...
        """
        sensors_file_path = os.path.join(OM_STORAGE_DIR, sensors_file_name)
        LOG.info("Writing TVB sensors to .txt file: %s" % sensors_file_path)
        numpy.savetxt(sensors_file_path, self.skin_air.sensor_locations)
        LOG.info("%s written successfully." % sensors_file_name)
        return sensors_file_path


    #TODO: enable specifying ?or determining? domain surface relationships... 
    def _write_head_geometry(self, boundary_file_names, geom_file_name):
        """
        Write a geometry file that is read in by OpenMEEG, this file specifies
        the files containng the boundary surfaces and there relationship to the
        domains that comprise the head.

        NOTE: Currently the list of files is expected to be in a specific order, 
        namely::

            skull_skin
            brain_skull
            skin_air

        which is reflected in the static setting of domains. Should be generalised.
        """

        geom_file_path = os.path.join(OM_STORAGE_DIR, geom_file_name)

        #TODO: Check that the file doesn't already exist.
        LOG.info("Writing head geometry file: %s" % geom_file_path)
        file_handle = file(geom_file_path, "a")

        file_handle.write("# Domain Description 1.0\n\n")
        file_handle.write("Interfaces %d Mesh\n\n" % len(boundary_file_names))

        for file_name in boundary_file_names:
            file_handle.write("%s\n" % file_name)

        file_handle.write("\nDomains %d\n\n" % (len(boundary_file_names) + 1))
        file_handle.write("Domain Scalp %s %s\n" % (1, -3))
        file_handle.write("Domain Brain %s %s\n" % ("-2", "shared"))
        file_handle.write("Domain Air %s\n" % 3)
        file_handle.write("Domain Skull %s %s\n" % (2, -1))

        file_handle.close()
        LOG.info("%s written successfully." % geom_file_path)

        return geom_file_path


    def _write_conductances(self, cond_file_name):
        """
        Write a conductance file that is read in by OpenMEEG, this file 
        specifies the conductance of each of the domains making up the head.

        NOTE: Vaules are restricted to have 2 decimal places, ie #.##, setting 
            values of the form 0.00# will result in 0.01 or 0.00, for numbers 
            greater or less than ~0.00499999999999999967, respecitvely...

        """
        cond_file_path = os.path.join(OM_STORAGE_DIR, cond_file_name)

        #TODO: Check that the file doesn't already exist.
        LOG.info("Writing head conductance file: %s" % cond_file_path)
        file_handle = file(cond_file_path, "a")

        file_handle.write("# Properties Description 1.0 (Conductivities)\n\n")
        file_handle.write("Air         %4.2f\n" % self.conductances["air"])
        file_handle.write("Scalp       %4.2f\n" % self.conductances["skin"])
        file_handle.write("Brain       %4.2f\n" % self.conductances["brain"])
        file_handle.write("Skull       %4.2f\n" % self.conductances["skull"])

        file_handle.close()
        LOG.info("%s written successfully." % cond_file_path)

        return cond_file_path


    #TODO: Either make these utility functions or have them load directly into
    #      the appropriate attribute...
    ##------------------------------------------------------------------------##
    ##---- Methods for loading precomputed matrices into openmeeg objects ----##
    ##------------------------------------------------------------------------##

    def _load_om_inverse_head_mat(self, file_name):
        """
        Load a previously stored inverse head matrix into an OpenMEEG SymMatrix
        object.
        """
        inverse_head_martix = om.SymMatrix()
        inverse_head_martix.load(file_name)
        return inverse_head_martix


    def _load_om_source_mat(self, file_name):
        """
        Load a previously stored source matrix into an OpenMEEG Matrix object.
        """
        source_matrix = om.Matrix()
        source_matrix.load(file_name)
        return source_matrix
예제 #28
0
    def configure(self,
                  dt=2**-3,
                  model=models.Generic2dOscillator,
                  speed=4.0,
                  coupling_strength=0.00042,
                  method=HeunDeterministic,
                  surface_sim=False,
                  default_connectivity=True):
        """
        Create an instance of the Simulator class, by default use the
        generic plane oscillator local dynamic model and the deterministic 
        version of Heun's method for the numerical integration.
        
        """
        self.method = method

        if default_connectivity:
            white_matter = Connectivity.from_file()
            region_mapping = RegionMapping.from_file(
                source_file="regionMapping_16k_76.txt")
        else:
            white_matter = Connectivity.from_file(
                source_file="connectivity_192.zip")
            region_mapping = RegionMapping.from_file(
                source_file="regionMapping_16k_192.txt")
        region_mapping.surface = CorticalSurface.from_file()

        white_matter_coupling = coupling.Linear(
            a=numpy.array([coupling_strength]))
        white_matter.speed = numpy.array(
            [speed])  # no longer allow scalars to numpy array promotion

        dynamics = model()

        if issubclass(method, IntegratorStochastic):
            hisss = noise.Additive(nsig=numpy.array([2**-11]))
            integrator = method(dt=dt, noise=hisss)
        else:
            integrator = method(dt=dt)

        if surface_sim:
            local_coupling_strength = numpy.array([2**-10])
            default_cortex = Cortex.from_file()
            default_cortex.region_mapping_data = region_mapping
            default_cortex.coupling_strength = local_coupling_strength
            if default_connectivity:
                default_cortex.local_connectivity = LocalConnectivity.from_file(
                )
            else:
                default_cortex.local_connectivity = LocalConnectivity()
            default_cortex.local_connectivity.surface = default_cortex.region_mapping_data.surface
        else:
            default_cortex = None

        # Order of monitors determines order of returned values.
        self.sim = simulator.Simulator()
        self.sim.surface = default_cortex
        self.sim.model = dynamics
        self.sim.integrator = integrator
        self.sim.connectivity = white_matter
        self.sim.coupling = white_matter_coupling
        self.sim.monitors = self.monitors
        self.sim.configure()
예제 #29
0
class Head(HasTraits):
    """
    One patient virtualization. Fully configured for defining hypothesis on it.
    """
    # TODO: find a solution with cross-references between tvb-scripts and TVB datatypes
    title = Attr(str, default="Head", required=False)
    path = Attr(str, default="path", required=False)
    connectivity = Attr(field_type=TVBConnectivity)
    cortical_surface = Attr(field_type=TVBSurface, required=False)
    subcortical_surface = Attr(field_type=TVBSurface, required=False)
    cortical_region_mapping = Attr(field_type=TVBRegionMapping, required=False)
    subcortical_region_mapping = Attr(field_type=TVBRegionMapping, required=False)
    region_volume_mapping = Attr(field_type=TVBRegionVolumeMapping, required=False)
    local_connectivity = Attr(field_type=TVBLocalConnectivity, required=False)
    t1 = Attr(field_type=TVBStructuralMRI, required=False)
    t2 = Attr(field_type=TVBStructuralMRI, required=False)
    flair = Attr(field_type=TVBStructuralMRI, required=False)
    b0 = Attr(field_type=TVBStructuralMRI, required=False)
    eeg_sensors = Attr(field_type=TVBSensors, required=False)
    seeg_sensors = Attr(field_type=TVBSensors, required=False)
    meg_sensors = Attr(field_type=TVBSensors, required=False)
    eeg_projection = Attr(field_type=TVBProjectionMatrix, required=False)
    seeg_projection = Attr(field_type=TVBProjectionMatrix, required=False)
    meg_projection = Attr(field_type=TVBProjectionMatrix, required=False)
    _cortex = None

    def __init__(self, **kwargs):
        super(Head, self).__init__(**kwargs)

    def configure(self):
        if isinstance(self.connectivity, TVBConnectivity):
            self.connectivity.configure()
        if isinstance(self.connectivity, TVBLocalConnectivity):
            self.local_connectivity.configure()
        if isinstance(self.cortical_surface, TVBSurface):
            self.cortical_surface.configure()
            if not isinstance(self.cortical_surface, TVBCorticalSurface):
                self.log.warning("cortical_surface is not an instance of TVB CorticalSurface!")
            if isinstance(self.cortical_region_mapping, TVBRegionMapping):
                self.cortical_region_mapping.connectivity = self.connectivity
                self.cortical_region_mapping.surface = self.cortical_surface
                self.cortical_region_mapping.configure()
        if isinstance(self.subcortical_surface, TVBSurface):
            self.subcortical_surface.configure()
            if not isinstance(self.subcortical_surface, CorticalSurface):
                self.log.warning("cortical_surface is not an instance of SubcorticalSurface!")
            if isinstance(self.subcortical_region_mapping, TVBRegionMapping):
                self.subcortical_region_mapping.connectivity = self.connectivity
                self.subcortical_region_mapping.surface = self.subcortical_surface
                self.subcortical_region_mapping.configure()
        structural = None
        for s_type in ["b0", "flair", "t2", "t1"]:
            instance = getattr(self, s_type)
            if isinstance(instance, TVBStructuralMRI):
                instance.configure()
                structural = instance
        if structural is not None:
            if isinstance(self.region_volume_mapping, TVBRegionVolumeMapping):
                self.region_volume_mapping.connectivity = self.connectivity
                self.region_volume_mapping.volume = structural.volume
                self.region_volume_mapping.configure()
        for s_type, p_type, s_datatype, p_datatype \
                in zip(["eeg", "seeg", "meg"],
                       [ProjectionsType.EEG.value, ProjectionsType.SEEG.value, ProjectionsType.MEG.value],
                       [TVBSensorsEEG, TVBSensorsInternal, TVBSensorsMEG],
                       [TVBProjectionSurfaceEEG, TVBProjectionSurfaceSEEG, TVBProjectionSurfaceMEG]):
            sensor_name = "%s_sensors" % s_type
            sensors = getattr(self, sensor_name)
            if isinstance(sensors, TVBSensors):
                sensors.configure()
                if not isinstance(sensors, s_datatype):
                    self.log.warning("%s is not an instance of TVB %s!" % (sensor_name, s_datatype.__name__))
                projection_name = "%s_projection" % s_type
                projection = getattr(self, projection_name)
                if isinstance(projection, TVBProjectionMatrix):
                    projection.sensors = sensors
                    if not isinstance(projection, p_datatype):
                        self.log.warning("%s is not an instance of TVB %s!" % (projection_name, p_datatype.__name__))
                    if isinstance(self.surface, Surface):
                        projection.sources = self.surface
                    projection.projection_type = p_type
                    projection.configure()

    def filter_regions(self, filter_arr):
        return self.connectivity.region_labels[filter_arr]

    def _get_filepath(self, filename, patterns, used_filepaths):
        # Search for default names if there is no filename provided
        if filename is None:
            for pattern in patterns:
                filepaths = insensitive_glob(os.path.join(self.path, "*%s*" % pattern))
                if len(filepaths) > 0:
                    for filepath in filepaths:
                        if filepath not in used_filepaths and os.path.isfile(filepath):
                            return filepath
            return None
        else:
            try:
                return insensitive_glob(os.path.join(self.path, "*%s*" % filename))[0]
            except:
                self.log.warning("No *%s* file found in %s path!" % (filename, self.path))

    def _load_reference(self, datatype, arg_name, patterns, used_filepaths, **kwargs):
        # Load from file
        filepath = self._get_filepath(kwargs.pop(arg_name, None), patterns, used_filepaths)
        if filepath is not None:
            used_filepaths.append(filepath)
            if issubclass(datatype, BaseModel):
                if filepath.endswith("h5"):
                    return datatype.from_h5_file(filepath), kwargs
                else:
                    return datatype.from_tvb_file(filepath), kwargs
            else:
                return datatype.from_file(filepath), kwargs
        else:
            return None, kwargs

    @classmethod
    def from_folder(cls, path=None, head=None, **kwargs):
        # TODO confirm the filetypes and add (h5 and other) readers to all TVB classes .from_file methods
        # Default patterns:
        # *conn* for zip/h5 files
        # (*cort/subcort*)surf*(*cort/subcort*) / (*cort/subcort*)srf*(*cort/subcort*) for zip/h5 files
        # (*cort/subcort*)reg*map(*cort/subcort*) for txt files
        # *map*vol* / *vol*map* for txt files
        # *t1/t2/flair/b0 for ??? files
        # *eeg/seeg/meg*sensors/locations* / *sensors/locations*eeg/seeg/meg for txt files
        # # *eeg/seeg/meg*proj/gain* / *proj/gain*eeg/seeg/meg for npy/mat

        used_filepaths = []

        if head is None:
            head = Head()
            head.path = path
            title = os.path.basename(path)
            if len(title) > 0:
                head.title = title

        # We need to read local_connectivity first to avoid confusing it with connectivity:
        head.local_connectivity, kwargs = \
            head._load_reference(LocalConnectivity, 'local_connectivity', ["loc*conn", "conn*loc"],
                                 used_filepaths, **kwargs)

        # Connectivity is required
        # conn_instances
        connectivity, kwargs = \
            head._load_reference(Connectivity, "connectivity", ["conn"], used_filepaths, **kwargs)
        if connectivity is None:
            raise_value_error("A Connectivity instance is minimally required for a Head instance!", cls.log)
        head.connectivity = connectivity

        # TVB only volume datatypes: do before region_mappings to avoid confusing them with volume_mapping
        structural = None
        for datatype, arg_name, patterns in zip([B0, Flair, T2, T1],
                                                ["b0", "flair", "t2", "t1", ],
                                                [["b0"], ["flair"], ["t2"], ["t1"]]):
            try:
                datatype.from_file
                instance, kwargs = head._load_reference(datatype, arg_name, patterns, used_filepaths, **kwargs)
            except:
                cls.log.warning("No 'from_file' method yet for %s!" % datatype.__class__.__name__)
                instance = None
            if instance is not None:
                setattr(head, arg_name, instance)
                volume_instance = instance
        if structural is not None:
            head.region_volume_mapping, kwargs = \
                head._load_reference(RegionVolumeMapping, "region_volume_mapping", ["vol*map", "map*vol"],
                                     used_filepaths, **kwargs)

        # Surfaces and mappings
        # (read subcortical ones first to avoid confusion):
        head.subcortical_surface, kwargs = \
            head._load_reference(SubcorticalSurface, "subcortical_surface",
                                 ["subcort*surf", "surf*subcort", "subcort*srf", "srf*subcort"],
                                 used_filepaths, **kwargs)
        if head.subcortical_surface is not None:
            # Region Mapping requires Connectivity and Surface
            head.subcortical_region_mapping, kwargs = \
                head._load_reference(SubcorticalRegionMapping, "subcortical_region_mapping",
                                     ["subcort*reg*map", "reg*map*subcort"],
                                     used_filepaths, **kwargs)

        head.cortical_surface, kwargs = \
            head._load_reference(CorticalSurface, "cortical_surface",
                                 ["cort*surf", "surf*cort", "cort*srf", "srf*cort", "surf", "srf"],
                                 used_filepaths, **kwargs)
        if head.cortical_surface is not None:
            # Region Mapping requires Connectivity and Surface
            head.cortical_region_mapping, kwargs = \
                head._load_reference(CorticalRegionMapping, "cortical_region_mapping",
                                     ["cort*reg*map", "reg*map*cort", "reg*map"], used_filepaths, **kwargs)

        # Sensors and projections
        # (read seeg before eeg to avoid confusion!)
        for s_datatype, p_datatype, s_type in zip([SensorsSEEG, SensorsEEG, SensorsMEG],
                                                  [ProjectionSurfaceSEEG, ProjectionSurfaceEEG, ProjectionSurfaceMEG],
                                                  ["seeg", "eeg", "meg"]):
            arg_name = "%s_sensors" % s_type
            patterns = ["%s*sensors" % s_type, "sensors*%s" % s_type,
                        "%s*locations" % s_type, "locations*%s" % s_type]
            sensors, kwargs = head._load_reference(s_datatype, arg_name, patterns, used_filepaths, **kwargs)
            if sensors is not None:
                setattr(head, arg_name, sensors)
                arg_name = "%s_projection" % s_type
                patterns = ["%s*proj" % s_type, "proj*%s" % s_type, "%s*gain" % s_type, "gain*%s" % s_type]
                projection, kwargs = head._load_reference(p_datatype, arg_name, patterns, used_filepaths, **kwargs)
                setattr(head, arg_name, projection)

        return head

    @classmethod
    def from_file(cls, path, **kwargs):
        filename = os.path.basename(path)
        dirname = os.path.dirname(path)
        if "head" in filename.lower():
            import h5py
            head = Head()
            head.path = path
            h5file = h5py.File(path, 'r', libver='latest')
            for field in []:
                try:
                    setattr(head, field, h5file['/' + field][()])
                except:
                    cls.log.warning("Failed to read Head field %s from file %s!" % (field, path))
            for attr in ["title"]:
                try:
                    setattr(head, attr, h5file.attrs.get(attr, h5file.attrs.get("TVB_%s" % attr)))
                except:
                    cls.log.warning("Failed to read Head attribute %s from file %s!" % (attr, path))
            head.path = dirname
        else:
            kwargs["connectivity"] = filename
            head = None
        return cls.from_folder(dirname, head, **kwargs)

    @classmethod
    def from_tvb_file(cls, path, **kwargs):
        return cls.from_file(path, **kwargs)

    def make_cortex(self, local_connectivity=None, coupling_strength=None):
        self._cortex = Cortex()
        self._cortex.region_mapping_data = self.cortical_region_mapping
        if isinstance(local_connectivity, LocalConnectivity):
            self._cortex.local_connectivity = local_connectivity
        if coupling_strength is not None:
            self._cortex.coupling_strength = coupling_strength
        self._cortex.configure()
        return self._cortex

    def cortex(self, local_connectivity=None, coupling_strength=None):
        if not isinstance(self._cortex, Cortex):
            self.make_cortex(local_connectivity, coupling_strength)
        return self._cortex

    @property
    def surface(self):
        return self.cortical_surface

    @property
    def number_of_regions(self):
        return self.connectivity.number_of_regions
예제 #30
0
#Initialise some Monitors with period in physical time
mon_tavg = monitors.TemporalAverage(period=2**-2)
mon_savg = monitors.SpatialAverage(period=2**-2)
mon_eeg = monitors.EEG(period=2**-2)

#Bundle them
what_to_watch = (mon_tavg, mon_savg, mon_eeg)

#Initialise a surface
local_coupling_strength = numpy.array([0.0121])

grey_matter = LocalConnectivity(equation=equations.Gaussian(), cutoff=60.0)
grey_matter.equation.parameters['sigma'] = 10.0
grey_matter.equation.parameters['amp'] = 1.0

default_cortex = Cortex.from_file(
    eeg_projection_file="surface_reg_13_eeg_62.mat")
default_cortex.local_connectivity = grey_matter
default_cortex.coupling_strength = local_coupling_strength

#Define the stimulus
eqn_t = equations.Gaussian()
eqn_t.parameters["amp"] = 1.0
eqn_t.parameters["midpoint"] = 8.0
eqn_x = equations.Gaussian()

eqn_x.parameters["amp"] = -0.0625
eqn_x.parameters["sigma"] = 28.0

stimulus = patterns.StimuliSurface(surface=default_cortex,
                                   temporal=eqn_t,
                                   spatial=eqn_x,