Esempio n. 1
0
def test_transcription():
    parameters = {
        'sequence': toy_chromosome_config['sequence'],
        'templates': toy_chromosome_config['promoters'],
        'genes': toy_chromosome_config['genes'],
        'promoter_affinities': toy_chromosome_config['promoter_affinities'],
        'transcription_factors': ['tfA', 'tfB'],
        'elongation_rate': 10.0
    }

    chromosome = Chromosome(toy_chromosome_config)
    transcription = Transcription(parameters)

    experiment = process_in_experiment(
        transcription, {
            'initial_state': {
                'chromosome': chromosome.to_dict(),
                'molecules':
                {nucleotide: 10
                 for nucleotide in transcription.monomer_ids},
                'proteins': {
                    UNBOUND_RNAP_KEY: 10
                },
                'factors': {
                    'tfA': 0.2,
                    'tfB': 0.7
                }
            }
        })

    pp(experiment.state.get_value())
    experiment.update(10.0)
    pp(experiment.state.get_value())

    print('complete!')
Esempio n. 2
0
def generate_gfp_compartment(config):
    media = Media()
    PURE = {
        key: value * units.mmol / units.L
        for key, value in media.get_saved_media('PURE_Fuji_2014').items()
    }

    # TODO: deal with volume
    volume = 1e-15 * units.L
    mmol_to_counts = AVOGADRO.to('1/mmol') * volume.to('L')

    print(mmol_to_counts)

    PURE_counts = {
        key: int(value * mmol_to_counts)
        for key, value in PURE.items()
    }

    print(PURE)
    print(PURE_counts)

    plasmid = Chromosome(gfp_plasmid_config)
    sequences = plasmid.product_sequences()

    print(sequences)

    proteins = {
        UNBOUND_RNAP_KEY: PURE_counts[UNBOUND_RNAP_KEY],
        UNBOUND_RIBOSOME_KEY: PURE_counts[UNBOUND_RIBOSOME_KEY],
        'GFP': 0,
        'endoRNAse': 1
    }

    gfp_config = {
        'transcription': {
            'sequence': gfp_plasmid_config['sequence'],
            'templates': gfp_plasmid_config['promoters'],
            'genes': gfp_plasmid_config['genes'],
            'promoter_affinities': {
                ('T7', ): 0.001
            },
            'polymerase_occlusion': 30,
            'elongation_rate': 50
        },
        'translation': {
            'sequences': {
                'GFP_RNA': GFP.sequence
            },
            'templates': {
                'GFP_RNA': generate_template('GFP_RNA', len(GFP.sequence),
                                             ['GFP'])
            },
            'transcript_affinities': {
                'GFP_RNA': 0.001
            },
            'elongation_rate': 22,
            'polymerase_occlusion': 50
        },
        'degradation': {
            'sequences': sequences,
            'catalysis_rates': {  # TODO: provide kcat for each RNA variety
                'endoRNAse': 0
            },
            'degradation_rates': {
                'transcripts': {
                    'endoRNAse': {
                        'GFP_RNA': 1e-23
                    }
                }
            }
        },
        'initial_state': {
            'molecules': PURE_counts,
            'transcripts': {
                'GFP_RNA': 0
            },
            'proteins': proteins
        }
    }

    return GeneExpression(gfp_config)
Esempio n. 3
0
    def __init__(self, initial_parameters=None):
        '''A stochastic transcription model

        .. WARNING:: Vivarium's knowledge base uses the gene name to
            name the protein. This means that for a gene acrA that
            codes for protein AcrA, you must refer to the gene,
            transcript, and protein each as acrA.

        :term:`Ports`:

        * **chromosome**: The linked :term:`store` should hold a
          representation of the chromosome in the form returned by
          :py:meth:`vivarium.states.chromosome.Chromosome.to_dict`.
        * **molecules**: Expects variables with the names in the
          *molecule_ids* configuration. These are the monomers consumed
          by transcription.
        * **factors**: Expects variables for each transcription factor's
          concentration.
        * **transcripts**: The linked store should store the
          concentrations of the transcripts.
        * **proteins**: The linked store should hold the concentrations
          of the transcription factors and the RNA polymerase.

        Arguments:
            initial_parameters: The following configuration options may
                be provided:

                * **promoter_affinities** (:py:class:`dict`): Maps from
                  binding state tuples to the binding affinity of RNA
                  polymerase and the promoter when the promoter is at
                  that binding state. The binding state of a promoter is
                  which (if any) transcription factors are bound to the
                  promoter. Such a binding state can be represented by a
                  binding state tuple, which is a :py:class:`tuple`
                  whose first element is the name of the promoter. All
                  bound transcription factors are listed as subsequent
                  elements. If no transcription factors are bound, the
                  sole subsequent element is ``None``.

                  .. todo:: What is the significance of the order in the
                      binding state tuple?

                  .. todo:: What are the units of the affinities?

                * **transcription_factors** (:py:class:`list`): A list
                  of all modeled transcription factors.
                * **sequence**: The DNA sequence that includes all the
                  genes whose transcription is being modeled.
                * **templates** (:py:class:`dict`): Maps from the name
                  of an operon to that operon's :term:`template
                  specification`.
                * **genes** (:py:class:`dict`): Maps from operon name to
                  a list of the names of the genes in that operon.
                * **elongation_rate** (:py:class:`float`): The
                  elongation rate of the RNA polymerase.
                * **polymerase_occlusion** (:py:class:`int`): The number
                  of base pairs behind the polymerase where another
                  polymerase is occluded and so cannot bind.
                * **symbol_to_monomer** (:py:class:`dict`): Maps from
                  the symbols used to represent monomers in the RNA
                  sequence to the name of the free monomer. This should
                  generally be
                  :py:data:`vivarium.data.nucleotides.nucleotides`.
                * **monomer_ids** (:py:class:`list`): A list of the
                  names of the free monomers consumed by transcription.
                  This can generally be computed as:

                  >>> from vivarium.data.nucleotides import nucleotides
                  >>> monomer_ids = nucleotides.values()
                  >>> print(list(monomer_ids))
                  ['ATP', 'GTP', 'UTP', 'CTP']

                  Note that we only included the ``list()``
                  transformation to make the output prettier. The
                  ``dict_values`` object returned by the ``.values()``
                  call is sufficiently list-like for use here.
                * **molecule_ids** (:py:class:`list`): A list of all the
                  molecules needed by the :term:`process`. This will
                  generally be the same as *monomer_ids*.

        Example configuring the process (uses
        :py:func:`vivarium.library.pretty.format_dict`):

        >>> import random
        >>>
        >>> import numpy as np
        >>>
        >>> from vivarium.states.chromosome import (
        ...     toy_chromosome_config,
        ...     Chromosome,
        ... )
        >>> from vivarium.data.nucleotides import nucleotides
        >>> # format_dict lets us print dictionaries prettily
        >>> from vivarium.library.pretty import format_dict
        >>>
        >>> random.seed(0)  # Needed because process is stochastic
        >>> np.random.seed(0)
        >>> # We will use the toy chromosome from toy_chromosome_config
        >>> print(toy_chromosome_config)
        {'sequence': 'ATACGGCACGTGACCGTCAACTTA', 'genes': {'oA': ['eA'], 'oAZ': ['eA', 'eZ'], 'oB': ['eB'], 'oBY': ['eB', 'eY']}, 'promoter_order': ['pA', 'pB'], 'promoters': {'pA': {'id': 'pA', 'position': 3, 'direction': 1, 'sites': [{'position': 0, 'length': 3, 'thresholds': {'tfA': <Quantity(0.3, 'millimolar')>}}], 'terminators': [{'position': 6, 'strength': 0.5, 'products': ['oA']}, {'position': 12, 'strength': 1.0, 'products': ['oAZ']}]}, 'pB': {'id': 'pB', 'position': -3, 'direction': -1, 'sites': [{'position': 0, 'length': 3, 'thresholds': {'tfB': <Quantity(0.5, 'millimolar')>}}], 'terminators': [{'position': -9, 'strength': 0.5, 'products': ['oB']}, {'position': -12, 'strength': 1.0, 'products': ['oBY']}]}}, 'promoter_affinities': {('pA', None): 1.0, ('pA', 'tfA'): 10.0, ('pB', None): 1.0, ('pB', 'tfB'): 10.0}, 'domains': {0: {'id': 0, 'lead': 0, 'lag': 0, 'children': []}}, 'rnaps': {}}
        >>> monomer_ids = list(nucleotides.values())
        >>> configuration = {
        ...     'promoter_affinities': {
        ...         ('pA', None): 1.0,
        ...         ('pA', 'tfA'): 10.0,
        ...         ('pB', None): 1.0,
        ...         ('pB', 'tfB'): 10.0},
        ...     'transcription_factors': ['tfA', 'tfB'],
        ...     'sequence': toy_chromosome_config['sequence'],
        ...     'templates': toy_chromosome_config['promoters'],
        ...     'genes': toy_chromosome_config['genes'],
        ...     'elongation_rate': 10.0,
        ...     'polymerase_occlusion': 5,
        ...     'symbol_to_monomer': nucleotides,
        ...     'monomer_ids': monomer_ids,
        ...     'molecule_ids': monomer_ids,
        ... }
        >>> # At this point we haven't used the toy chromosome yet
        >>> # because it will be specified in the chromosome port.
        >>> # Notice that the parameters are specific to the chromosome.
        >>> transcription_process = Transcription(configuration)
        >>> # Now we need to initialize the simulation stores
        >>> state = {
        ...     'chromosome': toy_chromosome_config,
        ...     'molecules': {
        ...         nucleotide: 10
        ...         for nucleotide in monomer_ids
        ...     },
        ...     'proteins': {UNBOUND_RNAP_KEY: 10},
        ...     'factors': {'tfA': 0.2 * units.mM, 'tfB': 0.7 * units.mM},
        ... }
        >>> update = transcription_process.next_update(1.0, state)
        >>> print(update['chromosome'])
        {'rnaps': {'_add': [{'path': (2,), 'state': <class 'vivarium.states.chromosome.Rnap'>: {'id': 2, 'template': 'pA', 'template_index': 0, 'terminator': 1, 'domain': 0, 'state': 'polymerizing', 'position': 7}}, {'path': (3,), 'state': <class 'vivarium.states.chromosome.Rnap'>: {'id': 3, 'template': 'pB', 'template_index': 1, 'terminator': 0, 'domain': 0, 'state': 'occluding', 'position': 3}}, {'path': (4,), 'state': <class 'vivarium.states.chromosome.Rnap'>: {'id': 4, 'template': 'pA', 'template_index': 0, 'terminator': 0, 'domain': 0, 'state': 'occluding', 'position': 0}}], '_delete': []}, 'rnap_id': 4, 'domains': {0: <class 'vivarium.states.chromosome.Domain'>: {'id': 0, 'lead': 0, 'lag': 0, 'children': []}}, 'root_domain': 0}
        '''

        if not initial_parameters:
            initial_parameters = {}

        log.debug(
            'inital transcription parameters: {}'.format(initial_parameters))

        super(Transcription, self).__init__(initial_parameters)

        self.derive_defaults('templates', 'promoter_order', keys_list)
        self.derive_defaults('templates', 'transcript_ids', template_products)

        self.sequence = self.parameters['sequence']
        self.templates = self.parameters['templates']
        self.genes = self.parameters['genes']
        empty_chromosome = Chromosome({
            'sequence': self.sequence,
            'promoters': self.templates,
            'genes': self.genes
        })
        self.sequences = empty_chromosome.sequences()
        self.symbol_to_monomer = self.parameters['symbol_to_monomer']

        log.debug('chromosome sequence: {}'.format(self.sequence))

        self.promoter_affinities = self.parameters['promoter_affinities']
        self.promoter_order = self.parameters['promoter_order']
        self.promoter_count = len(self.promoter_order)

        self.transcription_factors = self.parameters['transcription_factors']
        self.molecule_ids = self.parameters['molecule_ids']
        self.molecule_ids.extend(['ATP', 'ADP'])
        self.monomer_ids = self.parameters['monomer_ids']
        self.transcript_ids = self.parameters['transcript_ids']
        self.elongation = 0
        self.elongation_rate = self.parameters['elongation_rate']
        self.polymerase_occlusion = self.parameters['polymerase_occlusion']

        self.stoichiometry = build_stoichiometry(self.promoter_count)
        self.initiation = StochasticSystem(self.stoichiometry,
                                           random_seed=np.random.randint(
                                               2**31))

        self.protein_ids = [UNBOUND_RNAP_KEY] + self.transcription_factors

        self.initial_domains = self.parameters['initial_domains']
        self.concentrations_deriver_key = self.parameters[
            'concentrations_deriver_key']

        self.chromosome_ports = ['rnaps', 'rnap_id', 'domains', 'root_domain']

        log.debug('final transcription parameters: {}'.format(self.parameters))
Esempio n. 4
0
    def next_update(self, timestep, states):
        chromosome_state = states['chromosome']
        # chromosome_state['rnaps'] = list(chromosome_state['rnaps'].values())
        original_rnap_keys = [
            rnap['id'] for rnap in chromosome_state['rnaps'].values()
        ]
        chromosome = Chromosome(self.chromosome_config(chromosome_state))

        molecules = states['molecules']
        proteins = states['proteins']
        factors = states['factors']  # as concentrations

        promoter_rnaps = chromosome.promoter_rnaps()
        promoter_domains = chromosome.promoter_domains()

        # Find out how many promoters are currently blocked by a
        # newly initiated or occluding rnap
        promoter_count = len(chromosome.promoter_order)
        blocked_promoters = np.zeros(promoter_count, dtype=np.int64)
        open_domains = {}
        bound_domains = {}
        for promoter_index, promoter_key in enumerate(
                chromosome.promoter_order):
            domains = []
            for rnap in promoter_rnaps.get(promoter_key, {}).values():
                if rnap.is_occluding():
                    domains.append(rnap.domain)
                    blocked_promoters[promoter_index] += 1

            bound_domains[promoter_key] = set(domains)
            open_domains[promoter_key] = promoter_domains[
                promoter_key] - bound_domains[promoter_key]

        blocked_promoters = np.array(blocked_promoters)

        # Make the state for a gillespie simulation out of total number of each
        # promoter by copy number not blocked by initiated rnap,
        # concatenated with the number of each promoter that is bound by rnap.
        # These are the two states for each promoter the simulation
        # will operate on, essentially going back and forth between
        # bound and unbound states.
        copy_numbers = chromosome.promoter_copy_numbers()
        original_unbound_rnaps = proteins[UNBOUND_RNAP_KEY]
        monomer_limits = {
            monomer: molecules[monomer]
            for monomer in self.monomer_ids
        }
        unbound_rnaps = original_unbound_rnaps

        time = 0
        now = 0
        elongation = Elongation(self.sequences, chromosome.promoters,
                                monomer_limits, self.symbol_to_monomer,
                                self.elongation)

        initiation_affinity = self.build_affinity_vector(
            chromosome.promoters, factors)

        while time < timestep:
            # build the state vector for the gillespie simulation
            substrate = np.concatenate([
                copy_numbers - blocked_promoters, blocked_promoters,
                [unbound_rnaps]
            ])

            log.debug('transcription substrate: {}'.format(substrate))
            log.debug('blocked promoters: {}'.format(blocked_promoters))

            # find number of monomers until next terminator
            distance = 1 / self.elongation_rate  # chromosome.terminator_distance()

            # find interval of time that elongates to the point of the next terminator
            interval = min(distance, timestep - time)

            if interval == distance:
                # perform the elongation until the next event
                terminations, monomer_limits, chromosome.rnaps = elongation.step(
                    interval, monomer_limits, chromosome.rnaps)
                unbound_rnaps += terminations
            else:
                elongation.store_partial(interval)
                terminations = 0

            log.debug('time: {} --- interval: {}'.format(time, interval))
            log.debug('monomer limits: {}'.format(monomer_limits))
            log.debug('terminations: {}'.format(terminations))

            # run simulation for interval of time to next terminator
            result = self.initiation.evolve(interval, substrate,
                                            initiation_affinity)

            log.debug('result: {}'.format(result))

            # perform binding
            for now, event in zip(result['time'], result['events']):
                # RNAP has bound the promoter
                promoter_key = chromosome.promoter_order[event]
                promoter = chromosome.promoters[promoter_key]
                domains = open_domains[promoter_key]
                domain = choose_element(domains)

                blocked_promoters[event] += 1
                bound_domains[promoter_key].add(domain)
                open_domains[promoter_key].remove(domain)

                # create a new bound RNAP and add it to the chromosome.
                new_rnap = chromosome.bind_rnap(event, domain)
                new_rnap.start_polymerizing()

                log.debug('newly bound RNAP: {}'.format(new_rnap))

                unbound_rnaps -= 1

            # deal with occluding rnap
            for rnap in chromosome.rnaps.values():
                if rnap.is_unoccluding(self.polymerase_occlusion):
                    log.debug('RNAP unoccluding: {}'.format(rnap))

                    blocked_promoters[rnap.template_index] -= 1
                    bound_domains[rnap.template].remove(rnap.domain)
                    open_domains[rnap.template].add(rnap.domain)
                    rnap.unocclude()
                log.debug('rnap: {}'.format(rnap))

            log.debug('complete: {}'.format(elongation.complete_polymers))

            time += interval

        # track how far elongation proceeded to start from next iteration
        self.elongation = elongation.elongation - int(elongation.elongation)

        proteins = {UNBOUND_RNAP_KEY: unbound_rnaps - original_unbound_rnaps}

        molecules = {
            key: count * -1
            for key, count in elongation.monomers.items()
        }

        # 1 ATP hydrolysis cost per nucleotide elongation
        molecules['ATP'] = 0
        molecules['ADP'] = 0
        for count in elongation.monomers.values():
            molecules['ATP'] -= count
            molecules['ADP'] += count

        chromosome_dict = chromosome.to_dict()
        rnaps = chromosome_dict['rnaps']

        original = set(original_rnap_keys)
        current = set(rnaps.keys())
        bound_rnaps = current - original
        completed_rnaps = original - current
        continuing_rnaps = original - completed_rnaps

        rnap_updates = {
            rnap_id: rnaps[rnap_id]
            for rnap_id in continuing_rnaps
        }

        add_rnaps = [{
            'path': (bound, ),
            'state': rnaps[bound]
        } for bound in bound_rnaps]

        delete_rnaps = [(completed, ) for completed in completed_rnaps]

        rnap_updates['_add'] = add_rnaps
        rnap_updates['_delete'] = delete_rnaps
        chromosome_dict['rnaps'] = rnap_updates

        update = {
            'chromosome':
            {key: chromosome_dict[key]
             for key in self.chromosome_ports},
            'proteins': proteins,
            'molecules': molecules,
            'transcripts': elongation.complete_polymers
        }

        log.debug('molecules update: {}'.format(update['molecules']))

        return update
Esempio n. 5
0
    def __init__(self, parameters={}):
        if self.knowledge_base is None:
            self.knowledge_base = KnowledgeBase()
        if self.ecoli_sequence is None:
            self.ecoli_sequence = read_sequence(ECOLI_GENOME_PATH)

        self.factor_thresholds = {
            ('flhDp', 'CRP'): 1e-05 * units.mM,
            ('fliLp1', 'flhDC'): 1e-06 * units.mM,
            ('fliLp1', 'fliA'): 1.3e-05 * units.mM,
            ('fliEp1', 'flhDC'): 4e-06 * units.mM,
            ('fliEp1', 'fliA'): 1.1e-05 * units.mM,
            ('fliFp1', 'flhDC'): 7e-06 * units.mM,
            ('fliFp1', 'fliA'): 1e-05 * units.mM,
            ('flgBp', 'flhDC'): 1e-05 * units.mM,
            ('flgBp', 'fliA'): 8e-06 * units.mM,
            ('flgAp', 'flhDC'): 1.3e-05 * units.mM,
            ('flgAp', 'fliA'): 6e-06 * units.mM,
            ('flhBp', 'flhDC'): 1.5e-05 * units.mM,
            ('flhBp', 'fliA'): 5e-06 * units.mM,
            ('fliAp1', 'flhDC'): 1.7e-05 * units.mM,
            ('fliAp1', 'fliA'): 4e-06 * units.mM,
            ('flgEp', 'flhDC'): 1.9e-05 * units.mM,
            ('flgEp', 'fliA'): 3e-06 * units.mM,
            ('fliDp', 'flhDC'): 1.9e-05 * units.mM,
            ('fliDp', 'fliA'): 3e-06 * units.mM,
            ('flgKp', 'flhDC'): 2.1e-05 * units.mM,
            ('flgKp', 'fliA'): 1e-06 * units.mM,
            ('fliCp', 'fliA'): 5e-06 * units.mM,
            ('tarp', 'fliA'): 7e-06 * units.mM,
            ('motAp', 'fliA'): 9e-06 * units.mM,
            ('flgMp', 'fliA'): 1.1e-06 * units.mM
        }

        self.factor_thresholds.update(parameters.get('thresholds', {}))

        self.chromosome_config = {
            'sequence': self.ecoli_sequence,
            'genes': {
                'flhDC': ['flhD', 'flhC'],
                'fliL':
                ['fliL', 'fliM', 'fliN', 'fliO', 'fliP', 'fliQ', 'fliR'],
                'fliE': ['fliE'],
                'fliF': ['fliF', 'fliG', 'fliH', 'fliI', 'fliJ', 'fliK'],
                'flgA': ['flgA', 'flgM', 'flgN'],
                'flgM': ['flgM', 'flgN'],
                'flgE': ['flgE'],
                'flgB': [
                    'flgB', 'flgC', 'flgD', 'flgE', 'flgF', 'flgG', 'flgH',
                    'flgI', 'flgJ'
                ],
                'flhB': ['flhB', 'flhA', 'flhE'],
                'fliA': ['fliA', 'fliZ'],  # ignore 'tcyJ' for now
                'fliD': ['fliD', 'fliS', 'fliT'],
                'flgK': ['flgK', 'flgL'],
                'fliC': ['fliC'],
                'tar': ['tar', 'tap', 'cheR', 'cheB', 'cheY', 'cheZ'],
                'motA': ['motA', 'motB', 'cheA', 'cheW']
            },
            'promoters': {
                'flhDp': {
                    'id':
                    'flhDp',
                    'position':
                    1978197,
                    'direction':
                    -1,
                    'sites': [{
                        'thresholds': {
                            'CRP': 1e-05
                        }
                    }],
                    'terminators': [{
                        'position': 1977266,
                        'strength': 1.0,
                        'products': ['flhDC']
                    }]
                },
                'fliLp1': {
                    'id':
                    'fliLp1',
                    'position':
                    2019618,
                    'direction':
                    1,
                    'sites': [{
                        'thresholds': {
                            'flhDC': 4e-06
                        }
                    }, {
                        'thresholds': {
                            'fliA': 1.3e-05
                        }
                    }],
                    'terminators': [{
                        'position': 2023678,
                        'strength': 1.0,
                        'products': ['fliL']
                    }]
                },
                'fliEp1': {
                    'id':
                    'fliEp1',
                    'position':
                    2013014,
                    'direction':
                    -1,
                    'sites': [{
                        'thresholds': {
                            'flhDC': 5e-06
                        }
                    }, {
                        'thresholds': {
                            'fliA': 1.1e-05
                        }
                    }],
                    'terminators': [{
                        'position': 2012700,
                        'strength': 1.0,
                        'products': ['fliE']
                    }]
                },
                'fliFp1': {
                    'id':
                    'fliFp1',
                    'position':
                    2013229,
                    'direction':
                    1,
                    'sites': [{
                        'thresholds': {
                            'flhDC': 7e-06
                        }
                    }, {
                        'thresholds': {
                            'fliA': 1e-05
                        }
                    }],
                    'terminators': [{
                        'position': 2019513,
                        'strength': 1.0,
                        'products': ['fliF']
                    }]
                },
                'flgBp': {
                    'id':
                    'flgBp',
                    'position':
                    1130863,
                    'direction':
                    -1,
                    'sites': [{
                        'thresholds': {
                            'flhDC': 1e-05
                        }
                    }, {
                        'thresholds': {
                            'fliA': 8e-06
                        }
                    }],
                    'terminators': [{
                        'position': 1129414,
                        'strength': 1.0,
                        'products': ['flgA']
                    }]
                },
                'flgAp': {
                    'id':
                    'flgAp',
                    'position':
                    1131018,
                    'direction':
                    1,
                    'sites': [{
                        'thresholds': {
                            'flhDC': 1.3e-05
                        }
                    }, {
                        'thresholds': {
                            'fliA': 6e-06
                        }
                    }],
                    'terminators': [{
                        'position': 1138312,
                        'strength': 1.0,
                        'products': ['flgB']
                    }]
                },
                'flhBp': {
                    'id':
                    'flhBp',
                    'position':
                    1966191,
                    'direction':
                    -1,
                    'sites': [{
                        'thresholds': {
                            'flhDC': 1.5e-05
                        }
                    }, {
                        'thresholds': {
                            'fliA': 5e-06
                        }
                    }],
                    'terminators': [{
                        'position': 1962580,
                        'strength': 1.0,
                        'products': ['flhB']
                    }]
                },
                'fliAp1': {
                    'id':
                    'fliAp1',
                    'position':
                    2001789,
                    'direction':
                    -1,
                    'sites': [{
                        'thresholds': {
                            'flhDC': 1.7e-05
                        }
                    }, {
                        'thresholds': {
                            'fliA': 4e-06
                        }
                    }],
                    'terminators': [{
                        'position': 1999585,
                        'strength': 1.0,
                        'products': ['fliA']
                    }]
                },
                'flgEp': {
                    'id':
                    'flgEp',
                    'position':
                    1132574,
                    'direction':
                    1,
                    'sites': [{
                        'thresholds': {
                            'flhDC': 1.9e-05
                        }
                    }, {
                        'thresholds': {
                            'fliA': 3e-06
                        }
                    }],
                    'terminators': [{
                        'position': 1133782,
                        'strength': 1.0,
                        'products': ['flgE']
                    }]
                },
                'fliDp': {
                    'id':
                    'fliDp',
                    'position':
                    2003872,
                    'direction':
                    1,
                    'sites': [{
                        'thresholds': {
                            'flhDC': 1.9e-05
                        }
                    }, {
                        'thresholds': {
                            'fliA': 3e-06
                        }
                    }],
                    'terminators': [{
                        'position': 2006078,
                        'strength': 1.0,
                        'products': ['fliD']
                    }]
                },
                'flgKp': {
                    'id':
                    'flgKp',
                    'position':
                    1138378,
                    'direction':
                    1,
                    'sites': [{
                        'thresholds': {
                            'flhDC': 2.1e-05
                        }
                    }, {
                        'thresholds': {
                            'fliA': 1e-06
                        }
                    }],
                    'terminators': [{
                        'position': 1140986,
                        'strength': 1.0,
                        'products': ['flgK']
                    }]
                },
                'fliCp': {
                    'id':
                    'fliCp',
                    'position':
                    2002110,
                    'direction':
                    1,
                    'sites': [{
                        'thresholds': {
                            'fliA': 5e-06
                        }
                    }],
                    # {'thresholds': {'GadE': 0.55, 'H-NS': 0.6}}],
                    'terminators': [{
                        'position': 2003606,
                        'strength': 1.0,
                        'products': ['fliC']
                    }]
                },
                'tarp': {
                    'id':
                    'tarp',
                    'position':
                    1972691,
                    'direction':
                    -1,
                    'sites': [{
                        'thresholds': {
                            'fliA': 7e-06
                        }
                    }],
                    # {'thresholds': {'Fnr': 1e-5}}
                    'terminators': [{
                        'position': 1971030,
                        'strength': 1.0,
                        'products': ['tar']
                    }]
                },
                'motAp': {
                    'id':
                    'motAp',
                    'position':
                    1977139,
                    'direction':
                    -1,
                    'sites': [{
                        'thresholds': {
                            'fliA': 9e-06
                        }
                    }],
                    # {'thresholds': {'CpxR': 1e-5}}],
                    'terminators': [{
                        'position': 1976252,
                        'strength': 1.0,
                        'products': ['motA']
                    }]
                },
                'flgMp': {
                    'id':
                    'flgMp',
                    'position':
                    1130128,
                    'direction':
                    -1,
                    'sites': [{
                        'thresholds': {
                            'fliA': 1.1e-05
                        }
                    }],
                    # {'thresholds': {'CsgD': 0.1}}
                    'terminators': [{
                        'position': 1129414,
                        'strength': 1.0,
                        'products': ['flgM']
                    }]
                }
            },
            'domains': {
                0: {
                    'id': 0,
                    'lead': 0,
                    'lag': 0,
                    'children': []
                }
            },
            'rnaps': {}
        }

        # build chromosome and apply thresholds
        self.chromosome = Chromosome(self.chromosome_config)
        self.chromosome.apply_thresholds(self.factor_thresholds)
        self.chromosome_config['promoters'] = {
            key: promoter.to_dict()
            for key, promoter in self.chromosome.promoters.items()
        }

        self.promoters = [
            'flhDp', 'fliLp1', 'fliEp1', 'fliFp1', 'flgAp', 'flgBp', 'flhBp',
            'fliAp1', 'fliDp', 'flgKp', 'fliCp', 'tarp', 'motAp'
            'flgMp'
        ]

        self.flhDC_activated = [
            'fliLp1', 'fliEp1', 'fliFp1', 'flgAp', 'flgBp', 'flgEp', 'flhBp',
            'fliAp1', 'fliDp', 'flgKp'
        ]

        self.fliA_activated = ['fliCp', 'tarp', 'motAp', 'flgMp']

        flhDC_factors = {
            'fliLp1': {
                'flhDC': 1.2,
                'fliA': 0.25
            },
            'fliEp1': {
                'flhDC': 0.45,
                'fliA': 0.35
            },
            'fliFp1': {
                'flhDC': 0.35,
                'fliA': 0.30
            },
            'flgBp': {
                'flhDC': 0.35,
                'fliA': 0.45
            },
            'flgAp': {
                'flhDC': 0.15,
                'fliA': 0.3
            },
            'flgEp': {
                'flhDC': 1.0,
                'fliA': 4.0
            },
            'flhBp': {
                'flhDC': 0.1,
                'fliA': 0.35
            },
            'fliAp1': {
                'flhDC': 1.0,
                'fliA': 0.3
            },
            'fliDp': {
                'flhDC': 1.2,
                'fliA': 0.25
            },
            'flgKp': {
                'flhDC': 1.2,
                'fliA': 0.25
            }
        }

        def binary_sum_gates(promoter_factors):
            affinities = {}
            first, second = list(promoter_factors[list(
                promoter_factors.keys())[0]].keys())

            # this hard coding of simple addition is alarming and probably points
            # towards providing a function of promoter state for affinity rather
            # than a simple lookup of the affinity for each promoter state tuple.
            for promoter, factors in promoter_factors.items():
                affinities[(promoter, first, None)] = factors[first]
                affinities[(promoter, None, second)] = factors[second]
                affinities[(promoter, first,
                            second)] = factors[first] + factors[second]

            return affinities

        self.promoter_affinities = {('flhDp', 'CRP'): 0.01}

        # self.promoter_affinities[('motAp', 'CpxR')] = 1.0

        flhDC_affinities = binary_sum_gates(flhDC_factors)
        self.promoter_affinities.update(flhDC_affinities)

        # for promoter in self.flhDC_activated:
        #     self.promoter_affinities[(promoter, 'flhDC')] = 1.0

        for promoter in self.fliA_activated:
            self.promoter_affinities[(promoter, 'fliA')] = 1.0

        self.promoter_affinities.update(
            parameters.get('promoter_affinities', {}))

        self.transcripts = [
            (operon, product)
            for operon, products in self.chromosome_config['genes'].items()
            for product in products
        ]

        self.protein_sequences = {
            (operon, product): self.knowledge_base.proteins[
                self.knowledge_base.genes[product]['id']]['seq']
            for operon, product in self.transcripts
        }

        self.transcript_templates = {
            key: generate_template(key, len(sequence), [key[1]])
            for key, sequence in self.protein_sequences.items()
        }

        # transcript affinities are the affinities transcripts to bind a ribosome and translate to protein
        # transcript affinities are scaled relative to the requirements to build a single full flagellum.
        self.min_tr_affinity = parameters.get('min_tr_affinity', 1e-1)
        tr_affinity_scaling = {
            'fliL': 2,
            'fliM': 34,
            'fliG': 26,
            'fliH': 12,
            'fliI': 6,
            'fliD': 5,
            'flgE': 120
        }
        self.transcript_affinities = {}
        for (operon, product) in self.transcripts:
            self.transcript_affinities[(
                operon,
                product)] = self.min_tr_affinity * tr_affinity_scaling.get(
                    product, 1)
        self.transcript_affinities.update(
            parameters.get('transcript_affinities', {}))

        self.transcription_factors = [
            'flhDC', 'fliA', 'CsgD', 'CRP', 'GadE', 'H-NS', 'CpxR', 'Fnr'
        ]

        self.complexation_monomer_ids = [
            'fliG', 'fliM', 'fliN', 'flhA', 'flhB', 'flhD', 'flhC', 'fliO',
            'fliP', 'fliQ', 'fliR', 'fliJ', 'fliI', 'fliH', 'fliL', 'flgH',
            'motA', 'motB', 'flgB', 'flgC', 'flgF', 'flgG', 'flgI', 'fliF',
            'fliE', 'fliC', 'flgL', 'flgK', 'fliD', 'flgE'
        ]

        self.complexation_complex_ids = [
            'flhDC', 'flagellar motor switch', 'flagella',
            'flagellar export apparatus', 'flagellar motor'
        ]

        self.complexation_stoichiometry = {
            'flhDC': {
                'flhD': -4.0,
                'flhC': -2.0,
                'flhDC': 1.0
            },
            'flagellar motor switch reaction': {
                'flagellar motor switch': 1.0,
                'fliG': -26.0,
                'fliM': -34.0,
                'fliN': -1.0
            },
            'flagellar export apparatus reaction': {
                'flagellar export apparatus': 1.0,
                'flhA': -1.0,
                'flhB': -1.0,
                'fliO': -1.0,
                'fliP': -1.0,
                'fliQ': -1.0,
                'fliR': -1.0,
                'fliJ': -1.0,
                'fliI': -6.0,
                'fliH': -12.0
            },
            'flagellar motor reaction': {
                'flagellar motor': 1.0,
                'flagellar motor switch': -1.0,
                'fliL': -2.0,
                'flgH': -1.0,
                'motA': -1.0,
                'motB': -1.0,
                'flgB': -1.0,
                'flgC': -1.0,
                'flgF': -1.0,
                'flgG': -1.0,
                'flgI': -1.0,
                'fliF': -1.0,
                'fliE': -1.0
            },
            'flagellum reaction': {
                'flagella': 1.0,
                'flagellar export apparatus': -1.0,
                'flagellar motor': -1.0,
                'fliC': -1.0,
                'flgL': -1.0,
                'flgK': -1.0,
                'fliD': -5.0,
                'flgE': -120.0
            }
        }

        reaction_default = 1e-5
        self.complexation_rates = {
            'flhDC': reaction_default,
            'flagellar motor switch reaction': reaction_default,
            'flagellar export apparatus reaction': reaction_default,
            'flagellar motor reaction': reaction_default,
            'flagellum reaction': reaction_default
        }