Exemple #1
0
class TestInitialDynamicComponentsComprehensively(unittest.TestCase):
    def setUp(self):
        self.model_file = os.path.join(os.path.dirname(__file__), 'fixtures',
                                       'test_dynamic_expressions.xlsx')
        self.model = Reader().run(self.model_file)[Model][0]
        de_simulation_config = SimulationConfig(time_max=10)
        wc_sim_config = WCSimulationConfig(de_simulation_config)
        multialgorithm_simulation = MultialgorithmSimulation(
            self.model, wc_sim_config)
        _, self.dynamic_model = multialgorithm_simulation.build_simulation()

    def test(self):
        # test all DynamicComponents that implement eval()

        ### Test DynamicExpressions ###
        # each one is tested using each of the objects it uses in some instance in self.model_file
        # DynamicFunction
        for id, dynamic_function in self.dynamic_model.dynamic_functions.items(
        ):
            expected_value = float(self.model.get_functions(id=id)[0].comments)
            numpy.testing.assert_approx_equal(dynamic_function.eval(0),
                                              expected_value)
        # test eval_dynamic_functions()
        for func_id, func_val in self.dynamic_model.eval_dynamic_functions(
                0).items():
            expected_value = float(
                self.model.get_functions(id=func_id)[0].comments)
            numpy.testing.assert_approx_equal(func_val, expected_value)
        a_func_id = list(self.dynamic_model.dynamic_functions)[0]
        for func_id, func_val in \
            self.dynamic_model.eval_dynamic_functions(0, functions_to_eval=[a_func_id]).items():
            expected_value = float(
                self.model.get_functions(id=func_id)[0].comments)
            numpy.testing.assert_approx_equal(func_val, expected_value)

        # DynamicStopCondition
        for id, dynamic_stop_condition in self.dynamic_model.dynamic_stop_conditions.items(
        ):
            expected_val_in_comment = self.model.get_stop_conditions(
                id=id)[0].comments
            if expected_val_in_comment == 'True':
                expected_value = True
            elif expected_val_in_comment == 'False':
                expected_value = False
            self.assertEqual(expected_value, dynamic_stop_condition.eval(0))

        # DynamicObservable
        for id, dynamic_observable in self.dynamic_model.dynamic_observables.items(
        ):
            expected_value = float(
                self.model.get_observables(id=id)[0].comments)
            numpy.testing.assert_approx_equal(dynamic_observable.eval(0),
                                              expected_value)
        # test eval_dynamic_observables()
        for obs_id, obs_val in self.dynamic_model.eval_dynamic_observables(
                0).items():
            expected_value = float(
                self.model.get_observables(id=obs_id)[0].comments)
            numpy.testing.assert_approx_equal(obs_val, expected_value)
        an_obs_id = list(self.dynamic_model.dynamic_observables)[0]
        for obs_id, obs_val in \
            self.dynamic_model.eval_dynamic_observables(0, observables_to_eval=[an_obs_id]).items():
            expected_value = float(
                self.model.get_observables(id=obs_id)[0].comments)
            numpy.testing.assert_approx_equal(obs_val, expected_value)

        # DynamicRateLaw
        for id, dynamic_rate_law in self.dynamic_model.dynamic_rate_laws.items(
        ):
            expected_value = float(self.model.get_rate_laws(id=id)[0].comments)
            numpy.testing.assert_approx_equal(dynamic_rate_law.eval(0),
                                              expected_value)

        ### Test DynamicComponents ###
        # DynamicCompartment
        for id, dynamic_compartment in self.dynamic_model.dynamic_compartments.items(
        ):
            expected_value = float(
                self.model.get_compartments(id=id)[0].comments)
            numpy.testing.assert_approx_equal(dynamic_compartment.eval(0),
                                              expected_value)

        # DynamicParameter
        for id, dynamic_parameter in self.dynamic_model.dynamic_parameters.items(
        ):
            expected_value = float(
                self.model.get_parameters(id=id)[0].comments)
            numpy.testing.assert_approx_equal(dynamic_parameter.eval(0),
                                              expected_value)

        # DynamicSpecies
        for id, dynamic_species in self.dynamic_model.dynamic_species.items():
            expected_value = float(self.model.get_species(id=id)[0].comments)
            numpy.testing.assert_approx_equal(dynamic_species.eval(0),
                                              expected_value)

    # FIX FOR DE-SIM CHANGES: need to make a few dynamic models to test all branches of get_stop_condition()
    # a dynamic model with no stop concitions
    # a dynamic model with stop condidtions that all evaluate false
    # a dynamic model with at least one stop condidtions that evaluates true
    def test_get_stop_condition(self):
        all_stop_conditions = self.dynamic_model.get_stop_condition()
        self.assertTrue(callable(all_stop_conditions))
        self.assertTrue(all_stop_conditions(0))
Exemple #2
0
class TestMultialgorithmSimulationStatically(unittest.TestCase):

    MODEL_FILENAME = os.path.join(os.path.dirname(__file__), 'fixtures', 'test_model.xlsx')

    def setUp(self):
        # read and initialize a model
        self.model = Reader().run(self.MODEL_FILENAME, ignore_extra_models=True)[Model][0]
        for conc in self.model.distribution_init_concentrations:
            conc.std = 0.
        PrepForWcSimTransform().run(self.model)
        de_simulation_config = SimulationConfig(time_max=10)
        self.wc_sim_config = WCSimulationConfig(de_simulation_config, dfba_time_step=1)
        self.multialgorithm_simulation = MultialgorithmSimulation(self.model, self.wc_sim_config)
        self.test_dir = tempfile.mkdtemp()
        self.results_dir = tempfile.mkdtemp(dir=self.test_dir)

    def tearDown(self):
        shutil.rmtree(self.test_dir)

    def test_init(self):
        self.model.submodels = []
        with self.assertRaises(MultialgorithmError):
            MultialgorithmSimulation(self.model, self.wc_sim_config)

    def test_prepare_skipped_submodels(self):
        multialgorithm_simulation = MultialgorithmSimulation(self.model, self.wc_sim_config)
        self.assertEqual(multialgorithm_simulation.skipped_submodels(), set())
        submodels_to_skip = ['submodel_1']
        self.wc_sim_config.submodels_to_skip = submodels_to_skip
        multialgorithm_simulation = MultialgorithmSimulation(self.model, self.wc_sim_config)
        self.assertEqual(multialgorithm_simulation.skipped_submodels(), set(submodels_to_skip))

        submodels_to_skip = ['no_such_submodel']
        self.wc_sim_config.submodels_to_skip = submodels_to_skip
        with self.assertRaisesRegex(MultialgorithmError,
                                    "'submodels_to_skip' contains submodels that aren't in the model:"):
            MultialgorithmSimulation(self.model, self.wc_sim_config)

    def test_molecular_weights_for_species(self):
        multi_alg_sim = self.multialgorithm_simulation
        expected = {
            'species_6[c]': float('nan'),
            'H2O[c]': 18.0152
        }
        actual = multi_alg_sim.molecular_weights_for_species(set(expected.keys()))
        self.assertEqual(actual['H2O[c]'], expected['H2O[c]'])
        self.assertTrue(np.isnan(actual['species_6[c]']))

        # add a species_type without a structure
        species_type_wo_structure = self.model.species_types.create(
            id='st_wo_structure',
            name='st_wo_structure')
        cellular_compartment = self.model.compartments.get(**{'id': 'c'})[0]
        species_wo_structure = self.model.species.create(
            species_type=species_type_wo_structure,
            compartment=cellular_compartment)
        species_wo_structure.id = species_wo_structure.gen_id()

        actual = multi_alg_sim.molecular_weights_for_species([species_wo_structure.id])
        self.assertTrue(np.isnan(actual[species_wo_structure.id]))

        # test obtain weights for all species
        actual = multi_alg_sim.molecular_weights_for_species()
        self.assertEqual(actual['H2O[c]'], expected['H2O[c]'])
        self.assertTrue(np.isnan(actual['species_6[c]']))
        self.assertEqual(len(actual), len(self.model.get_species()))

    def test_create_dynamic_compartments(self):
        self.multialgorithm_simulation.create_dynamic_compartments()
        self.assertEqual(set(['c', 'e']), set(self.multialgorithm_simulation.temp_dynamic_compartments))
        for id, dynamic_compartment in self.multialgorithm_simulation.temp_dynamic_compartments.items():
            self.assertEqual(id, dynamic_compartment.id)
            self.assertTrue(0 < dynamic_compartment.init_density)

    def test_prepare_dynamic_compartments(self):
        self.multialgorithm_simulation.create_dynamic_compartments()
        self.multialgorithm_simulation.init_species_pop_from_distribution()
        self.multialgorithm_simulation.local_species_population = \
            self.multialgorithm_simulation.make_local_species_population(retain_history=False)
        self.multialgorithm_simulation.prepare_dynamic_compartments()
        for dynamic_compartment in self.multialgorithm_simulation.temp_dynamic_compartments.values():
            self.assertTrue(dynamic_compartment._initialized())
            self.assertTrue(0 < dynamic_compartment.accounted_mass())
            self.assertTrue(0 < dynamic_compartment.mass())

    def test_init_species_pop_from_distribution(self):
        self.multialgorithm_simulation.create_dynamic_compartments()
        self.multialgorithm_simulation.init_species_pop_from_distribution()
        species_wo_init_conc = ['species_1[c]', 'species_3[c]']
        for species_id in species_wo_init_conc:
            self.assertEqual(self.multialgorithm_simulation.init_populations[species_id], 0)
        for concentration in self.model.get_distribution_init_concentrations():
            self.assertTrue(0 <= self.multialgorithm_simulation.init_populations[concentration.species.id])

        # todo: statistically evaluate sampled population
        # ensure that over multiple runs of init_species_pop_from_distribution():
        # mean(species population) ~= mean(volume) * mean(concentration)

    def test_make_local_species_population(self):
        self.multialgorithm_simulation.create_dynamic_compartments()
        self.multialgorithm_simulation.init_species_pop_from_distribution()
        local_species_population = self.multialgorithm_simulation.make_local_species_population()
        self.assertEqual(local_species_population._molecular_weights,
            self.multialgorithm_simulation.molecular_weights_for_species())

        # test the initial population slopes
        # continuous adjustments are only allowed on species used by continuous submodels
        used_by_continuous_submodels = \
            ['species_1[e]', 'species_2[e]', 'species_1[c]', 'species_2[c]', 'species_3[c]']
        adjustments = {species_id: 0. for species_id in used_by_continuous_submodels}
        self.assertEqual(local_species_population.adjust_continuously(1, adjustments), None)
        not_in_a_reaction = ['H2O[e]', 'H2O[c]']
        used_by_discrete_submodels = ['species_4[c]', 'species_5[c]', 'species_6[c]']
        adjustments = {species_id: 0. for species_id in used_by_discrete_submodels + not_in_a_reaction}
        with self.assertRaises(DynamicSpeciesPopulationError):
            local_species_population.adjust_continuously(2, adjustments)

    def test_set_simultaneous_execution_priorities(self):
        expected_order_of_sim_obj_classes = [SsaSubmodel,
                                             NrmSubmodel,
                                             DsaSubmodel,
                                             DfbaSubmodel,
                                             OdeSubmodel,
                                             MultialgorithmicCheckpointingSimObj]
        self.multialgorithm_simulation.set_simultaneous_execution_priorities()
        # ensure that expected_order_of_sim_obj_classes are arranged in decreasing priority
        for i in range(len(expected_order_of_sim_obj_classes) - 1):
            simulation_object_class = expected_order_of_sim_obj_classes[i]
            next_simulation_object_class = expected_order_of_sim_obj_classes[i+1]
            self.assertLess(simulation_object_class.metadata.class_priority,
                            next_simulation_object_class.metadata.class_priority)

    def test_initialize_components(self):
        self.multialgorithm_simulation.initialize_components()
        self.assertTrue(isinstance(self.multialgorithm_simulation.local_species_population,
                        LocalSpeciesPopulation))
        for dynamic_compartment in self.multialgorithm_simulation.temp_dynamic_compartments.values():
            self.assertTrue(isinstance(dynamic_compartment.species_population, LocalSpeciesPopulation))

    def test_initialize_infrastructure(self):
        self.multialgorithm_simulation.initialize_components()
        self.multialgorithm_simulation.initialize_infrastructure()
        self.assertTrue(isinstance(self.multialgorithm_simulation.dynamic_model, DynamicModel))

        de_simulation_config = SimulationConfig(time_max=10, output_dir=self.results_dir)
        wc_sim_config = WCSimulationConfig(de_simulation_config, dfba_time_step=1, checkpoint_period=10)
        multialg_sim = MultialgorithmSimulation(self.model, wc_sim_config)
        multialg_sim.initialize_components()
        multialg_sim.initialize_infrastructure()
        self.assertEqual(multialg_sim.checkpointing_sim_obj.checkpoint_dir, self.results_dir)
        self.assertTrue(multialg_sim.checkpointing_sim_obj.access_state_object is not None)
        self.assertTrue(isinstance(multialg_sim.checkpointing_sim_obj, MultialgorithmicCheckpointingSimObj))
        self.assertTrue(isinstance(multialg_sim.dynamic_model, DynamicModel))

    def test_build_simulation(self):
        de_simulation_config = SimulationConfig(time_max=10, output_dir=self.results_dir)
        wc_sim_config = WCSimulationConfig(de_simulation_config, dfba_time_step=1, checkpoint_period=10)
        multialgorithm_simulation = MultialgorithmSimulation(self.model, wc_sim_config)
        simulation_engine, _ = multialgorithm_simulation.build_simulation()
        # 3 objects: 2 submodels, and the checkpointing obj:
        expected_sim_objs = set(['CHECKPOINTING_SIM_OBJ', 'submodel_1', 'submodel_2'])
        self.assertEqual(expected_sim_objs, set(list(simulation_engine.simulation_objects)))
        self.assertEqual(type(multialgorithm_simulation.checkpointing_sim_obj),
                         MultialgorithmicCheckpointingSimObj)
        self.assertEqual(multialgorithm_simulation.dynamic_model.get_num_submodels(), 2)

        # check that submodels receive options
        dfba_options = dict(dfba='fast but inaccurate')
        ssa_options = dict(ssa='accurate but slow')
        options = {'DfbaSubmodel': dict(options=dfba_options),
                   'SsaSubmodel': dict(options=ssa_options)
                  }
        multialgorithm_simulation = MultialgorithmSimulation(self.model, wc_sim_config, options)
        multialgorithm_simulation.build_simulation()
        dfba_submodel = multialgorithm_simulation.dynamic_model.dynamic_submodels['submodel_1']
        ssa_submodel = multialgorithm_simulation.dynamic_model.dynamic_submodels['submodel_2']
        self.assertEqual(dfba_submodel.options, dfba_options)
        self.assertEqual(ssa_submodel.options, ssa_options)

        # test skipped submodel
        submodels_to_skip = ['submodel_2']
        self.wc_sim_config.submodels_to_skip = submodels_to_skip
        ma_sim = MultialgorithmSimulation(self.model, self.wc_sim_config)
        _, dynamic_model = ma_sim.build_simulation()
        expected_dynamic_submodels = set([sm.id for sm in self.model.get_submodels()]) - ma_sim.skipped_submodels()
        self.assertEqual(expected_dynamic_submodels, set(dynamic_model.dynamic_submodels))

        submodel_1 = self.model.submodels.get(id='submodel_1')[0]
        # WC:modeling_framework is not an instance of a modeling framework
        submodel_1.framework = onto['WC:modeling_framework']
        ma_sim = MultialgorithmSimulation(self.model, self.wc_sim_config)
        with self.assertRaisesRegex(MultialgorithmError, 'Unsupported lang_submodel framework'):
            ma_sim.build_simulation()

    def test_get_dynamic_compartments(self):
        expected_compartments = dict(
            submodel_1=['c', 'e'],
            submodel_2=['c']
        )
        self.multialgorithm_simulation.build_simulation()
        for submodel_id in ['submodel_1', 'submodel_2']:
            submodel = self.model.submodels.get_one(id=submodel_id)
            submodel_dynamic_compartments = self.multialgorithm_simulation.get_dynamic_compartments(submodel)
            self.assertEqual(set(submodel_dynamic_compartments.keys()), set(expected_compartments[submodel_id]))

    def test_str(self):
        self.multialgorithm_simulation.create_dynamic_compartments()
        self.multialgorithm_simulation.init_species_pop_from_distribution()
        self.multialgorithm_simulation.local_species_population = \
            self.multialgorithm_simulation.make_local_species_population(retain_history=False)
        self.assertIn('species_1[e]', str(self.multialgorithm_simulation))
        self.assertIn('model:', str(self.multialgorithm_simulation))
class TestModelUtilities(unittest.TestCase):
    def get_submodel(self, model, id_val):
        return model.submodels.get_one(id=id_val)

    MODEL_FILENAME = os.path.join(os.path.dirname(__file__), 'fixtures',
                                  'test_model.xlsx')

    def setUp(self):
        # read a model
        self.model = Reader().run(self.MODEL_FILENAME,
                                  ignore_extra_models=True)[wc_lang.Model][0]

    def test_find_private_species(self):
        # since set() operations are being used, this test does not ensure that the methods being
        # tested are deterministic and repeatable; repeatability should be tested by running the
        # code under different circumstances and ensuring identical output
        private_species = ModelUtilities.find_private_species(self.model)

        submodel_1_exp_species_ids = [
            'species_1[c]', 'species_1[e]', 'species_2[e]'
        ]
        mod1_expected_species = wc_lang.Species.get(submodel_1_exp_species_ids,
                                                    self.model.get_species())
        self.assertEqual(
            set(private_species[self.get_submodel(self.model, 'submodel_1')]),
            set(mod1_expected_species))

        submodel_2_exp_species_ids = [
            'species_5[c]', 'species_4[c]', 'species_6[c]'
        ]
        mod2_expected_species = wc_lang.Species.get(submodel_2_exp_species_ids,
                                                    self.model.get_species())
        self.assertEqual(
            set(private_species[self.get_submodel(self.model, 'submodel_2')]),
            set(mod2_expected_species))

        private_species = ModelUtilities.find_private_species(self.model,
                                                              return_ids=True)
        self.assertEqual(set(private_species['submodel_1']),
                         set(submodel_1_exp_species_ids))
        self.assertEqual(set(private_species['submodel_2']),
                         set(submodel_2_exp_species_ids))

    def test_find_shared_species(self):
        self.assertEqual(
            set(ModelUtilities.find_shared_species(self.model)),
            set(
                wc_lang.Species.get(
                    ['species_2[c]', 'species_3[c]', 'H2O[e]', 'H2O[c]'],
                    self.model.get_species())))

        self.assertEqual(
            set(ModelUtilities.find_shared_species(self.model,
                                                   return_ids=True)),
            set(['species_2[c]', 'species_3[c]', 'H2O[e]', 'H2O[c]']))

    def test_sample_copy_num_from_concentration(self):
        model = wc_lang.Model()

        submodel = model.submodels.create(
            id='submodel',
            framework=onto['WC:stochastic_simulation_algorithm'])

        compartment_c = model.compartments.create(
            id='c', init_volume=wc_lang.InitVolume(mean=1.))

        structure = wc_lang.ChemicalStructure(molecular_weight=10.)

        species_types = {}
        cus_species_types = {}
        for cu in wc_lang.DistributionInitConcentration.units.choices:
            id = str(cu).replace(' ', '_')
            species_types[id] = model.species_types.create(id=id,
                                                           structure=structure)
            cus_species_types[id] = cu

        for other in ['no_units', 'no_concentration', 'no_std']:
            species_types[other] = model.species_types.create(
                id=other, structure=structure)

        species = {}
        for key, species_type in species_types.items():
            species[key] = wc_lang.Species(species_type=species_type,
                                           compartment=compartment_c)
            species[key].id = species[key].gen_id()

        conc_value = 2_000.
        std_value = 0.
        for key, sp in species.items():
            if key in cus_species_types:
                wc_lang.DistributionInitConcentration(
                    species=sp,
                    mean=conc_value,
                    std=std_value,
                    units=cus_species_types[key])
            elif key == 'no_units':
                wc_lang.DistributionInitConcentration(species=sp,
                                                      mean=conc_value,
                                                      std=std_value)
            elif key == 'no_std':
                wc_lang.DistributionInitConcentration(
                    species=sp,
                    mean=conc_value,
                    std=float('NaN'),
                    units=cus_species_types['molecule'])
            elif key == 'no_concentration':
                continue

        conc_to_molecules = ModelUtilities.sample_copy_num_from_concentration
        random_state = numpy.random.RandomState()
        copy_number = conc_to_molecules(
            species['molecule'],
            species['molecule'].compartment.init_volume.mean, random_state)
        self.assertEqual(copy_number, conc_value)
        copy_number = conc_to_molecules(
            species['molar'], species['molar'].compartment.init_volume.mean,
            random_state)
        self.assertEqual(copy_number, conc_value * Avogadro)
        copy_number = conc_to_molecules(
            species['no_units'],
            species['no_units'].compartment.init_volume.mean, random_state)
        self.assertEqual(copy_number, conc_value * Avogadro)
        copy_number = conc_to_molecules(
            species['millimolar'],
            species['millimolar'].compartment.init_volume.mean, random_state)
        self.assertEqual(copy_number, 10**-3 * conc_value * Avogadro)
        copy_number = conc_to_molecules(
            species['micromolar'],
            species['micromolar'].compartment.init_volume.mean, random_state)
        self.assertEqual(copy_number, 10**-6 * conc_value * Avogadro)
        copy_number = conc_to_molecules(
            species['nanomolar'],
            species['nanomolar'].compartment.init_volume.mean, random_state)
        self.assertEqual(copy_number, 10**-9 * conc_value * Avogadro)
        copy_number = conc_to_molecules(
            species['picomolar'],
            species['picomolar'].compartment.init_volume.mean, random_state)
        self.assertEqual(copy_number, 10**-12 * conc_value * Avogadro)
        copy_number = conc_to_molecules(
            species['femtomolar'],
            species['femtomolar'].compartment.init_volume.mean, random_state)
        self.assertAlmostEqual(copy_number,
                               10**-15 * conc_value * Avogadro,
                               delta=1)
        copy_number = conc_to_molecules(
            species['attomolar'],
            species['attomolar'].compartment.init_volume.mean, random_state)
        self.assertAlmostEqual(copy_number,
                               10**-18 * conc_value * Avogadro,
                               delta=1)
        copy_number = conc_to_molecules(
            species['no_concentration'],
            species['no_concentration'].compartment.init_volume.mean,
            random_state)
        self.assertEqual(copy_number, 0)
        conc = species['no_std'].distribution_init_concentration
        copy_number = conc_to_molecules(
            species['no_std'], species['no_std'].compartment.init_volume.mean,
            random_state)
        self.assertNotEqual(copy_number, conc_value)

        with self.assertRaises(KeyError):
            conc_to_molecules(
                species['mol dm^-2'],
                species['no_concentration'].compartment.init_volume.mean,
                random_state)

        species_tmp = wc_lang.Species(species_type=species_type,
                                      compartment=compartment_c)
        species_tmp.id = species_tmp.gen_id()
        wc_lang.DistributionInitConcentration(
            species=species_tmp,
            mean=conc_value,
            std=std_value,
            units='not type(unit_registry.Unit)')
        with self.assertRaisesRegex(ValueError, 'Unsupported unit type'):
            conc_to_molecules(species_tmp,
                              species_tmp.compartment.init_volume.mean,
                              random_state)

        species_tmp2 = wc_lang.Species(species_type=species_type,
                                       compartment=compartment_c)
        species_tmp2.id = species_tmp2.gen_id()
        wc_lang.DistributionInitConcentration(
            species=species_tmp2,
            mean=conc_value,
            std=std_value,
            units=wc_lang.InitVolume.units.choices[0])
        with self.assertRaisesRegex(ValueError, 'Unsupported unit'):
            conc_to_molecules(species_tmp2,
                              species_tmp2.compartment.init_volume.mean,
                              random_state)

    def test_get_species_types(self):
        self.assertEqual(ModelUtilities.get_species_types([]), [])

        species_type_ids = [
            species_type.id for species_type in self.model.get_species_types()
        ]
        species_ids = [
            species.serialize() for species in self.model.get_species()
        ]
        self.assertEqual(sorted(ModelUtilities.get_species_types(species_ids)),
                         sorted(species_type_ids))

    def test_get_species_types(self):
        self.assertEqual(
            ModelUtilities.parse_species_id('species_type_id[compartment_id]'),
            ('species_type_id', 'compartment_id'))
        with self.assertRaisesRegex(ValueError, 'Species id format should be'):
            ModelUtilities.parse_species_id('compartment_id]')
        with self.assertRaisesRegex(ValueError, 'Species id format should be'):
            ModelUtilities.parse_species_id('[compartment_id]')
        with self.assertRaisesRegex(ValueError, 'Species id format should be'):
            ModelUtilities.parse_species_id('species_type_id[]')
        with self.assertRaisesRegex(ValueError, 'Species id format should be'):
            ModelUtilities.parse_species_id(
                'species_type_id[compartment_id]extra')