def calculateRequest(self): # Request all unique origins of replication and replication forks self.oriCs.requestAll() # If there are no active forks return activeDnaPoly = self.activeDnaPoly.allMolecules() if len(activeDnaPoly) == 0: return # Request all active replication forks self.activeDnaPoly.requestAll() # Get sequences for all active forks sequenceIdx, sequenceLength = activeDnaPoly.attrs( 'sequenceIdx', 'sequenceLength') sequences = buildSequences(self.sequences, sequenceIdx, sequenceLength, self._dnaPolymeraseElongationRate()) # Count number of each dNTP in sequences for the next timestep sequenceComposition = np.bincount( sequences[sequences != polymerize.PAD_VALUE], minlength=4) # If one dNTP is limiting then limit the request for the other three by the same ratio dNtpsTotal = self.dntps.total() maxFractionalReactionLimit = (np.fmin(1, dNtpsTotal / sequenceComposition)).min() # Request dNTPs self.dntps.requestIs(maxFractionalReactionLimit * sequenceComposition)
def test_buildSequences(self): # Base case padding = np.empty((20, 10)) padding.fill(P) allSequences = np.hstack( (np.random.randint(3, size=(20, 10)), padding)).astype(np.int8, copy=False) sequenceIndexes = np.array([0, 5, 8]) polymerizedLengths = np.array([0, 4, 9]) elngRate = 5 sequences = buildSequences(allSequences, sequenceIndexes, polymerizedLengths, elngRate) comparison_sequence = np.empty((3, elngRate)) comparison_sequence[0, :] = allSequences[0, 0:elngRate] comparison_sequence[1, :] = allSequences[5, 4:(4 + elngRate)] comparison_sequence[2, :] = allSequences[8, 9:(9 + elngRate)] assert_equal(sequences, comparison_sequence) # Un-padded case should throw exception allSequences = np.random.randint(3, size=(20, 10)).astype(np.int8, copy=False) sequenceIndexes = np.array([0, 5, 8]) polymerizedLengths = np.array([0, 4, 9]) elngRate = 5 # TODO: Define an exeption subclass if you want to check the real message self.assertRaises(Exception, buildSequences, allSequences, sequenceIndexes, polymerizedLengths, elngRate)
def calculateRequest(self): # Calculate elongation rate based on the current nutrients current_nutrients = self._external_states['Environment'].nutrients self.rnapElngRate = int(stochasticRound(self.randomState, self.rnaPolymeraseElongationRateDict[current_nutrients].asNumber(units.nt / units.s) * self.timeStepSec())) # Request all active RNA polymerases activeRnaPolys = self.activeRnaPolys.allMolecules() if len(activeRnaPolys) == 0: return self.activeRnaPolys.requestAll() # Determine total possible sequences of nucleotides that can be transcribed in this time step for each polymerase rnaIndexes, transcriptLengths = activeRnaPolys.attrs('rnaIndex', 'transcriptLength') sequences = buildSequences(self.rnaSequences, rnaIndexes, transcriptLengths, self.rnapElngRate) sequenceComposition = np.bincount(sequences[sequences != polymerize.PAD_VALUE], minlength = 4) # Calculate if any nucleotides are limited and request up to the number in the sequences or number available ntpsTotal = self.ntps.total() maxFractionalReactionLimit = np.fmin(1, ntpsTotal / sequenceComposition) self.ntps.requestIs(maxFractionalReactionLimit * sequenceComposition) self.writeToListener("GrowthLimits", "ntpPoolSize", self.ntps.total()) self.writeToListener("GrowthLimits", "ntpRequestSize", maxFractionalReactionLimit * sequenceComposition)
def evolveState(self): ## Module 1: Replication initiation # Get number of active DNA polymerases and oriCs activeDnaPoly = self.activeDnaPoly.molecules() activePolymerasePresent = (len(activeDnaPoly) > 0) oriCs = self.oriCs.molecules() n_oric = len(oriCs) n_chromosomes = self.full_chromosome.total()[0] # If there are no chromosomes and oriC's, return immediately if n_oric == 0 and n_chromosomes == 0: return # Get cell mass cellMass = (self.readFromListener("Mass", "cellMass") * units.fg) # Get critical initiation mass for simulation medium environment current_nutrients = self._external_states['Environment'].nutrients self.criticalInitiationMass = self.getDnaCriticalMass( self.nutrientToDoublingTime[current_nutrients]) # Calculate mass per origin of replication, and compare to critical # initiation mass. This is a rearrangement of the equation: # (Cell Mass)/(Number of origins) > Critical mass # If the above inequality holds true, initiate a round of chromosome # replication for every origin of replication massFactor = cellMass / self.criticalInitiationMass massPerOrigin = massFactor / n_oric replication_initiated = False # If conditions are true, initiate a round of replication on every # origin of replication if massPerOrigin >= 1.0 and self.partialChromosomes.counts().sum( ) == 0: replication_initiated = True # Get replication round indexes of active DNA polymerases if activePolymerasePresent: replicationRound = activeDnaPoly.attr('replicationRound') else: # Set to -1 to set values for new polymerases to 0 replicationRound = np.array([-1]) # Get chromosome indexes of current oriCs chromosomeIndexOriC = oriCs.attr('chromosomeIndex') # Calculate number of new DNA polymerases required (4 per origin) n_new_polymerase = 4 * n_oric # Add new polymerases and oriC's activeDnaPolyNew = self.activeDnaPoly.moleculesNew( "dnaPolymerase", n_new_polymerase) oriCsNew = self.oriCs.moleculesNew("originOfReplication", n_oric) # Calculate and set attributes of newly created polymerases sequenceIdx = np.tile(np.array([0, 1, 2, 3]), n_oric) sequenceLength = np.zeros(n_new_polymerase, dtype=np.int) replicationRound = np.ones( n_new_polymerase, dtype=np.int) * (replicationRound.max() + 1) # Polymerases inherit index of the OriC's they were initiated from chromosomeIndexPolymerase = np.repeat(chromosomeIndexOriC, 4) activeDnaPolyNew.attrIs( sequenceIdx=sequenceIdx, sequenceLength=sequenceLength, replicationRound=replicationRound, chromosomeIndex=chromosomeIndexPolymerase, ) # Calculate and set attributes of newly created oriCs # New OriC's share the index of the old OriC's they were # replicated from oriCsNew.attrIs(chromosomeIndex=chromosomeIndexOriC) # Write data from this module to a listener self.writeToListener("ReplicationData", "criticalMassPerOriC", massPerOrigin) self.writeToListener("ReplicationData", "criticalInitiationMass", self.criticalInitiationMass.asNumber(units.fg)) ## Module 2: replication elongation # If no active polymerases are present, return immediately # Note: the new DNA polymerases activated in the previous module are # not elongated until the next timestep. if len(activeDnaPoly) == 0: return # Build sequences to polymerize dNtpCounts = self.dntps.counts() sequenceIdx, sequenceLengths, massDiffDna = activeDnaPoly.attrs( 'sequenceIdx', 'sequenceLength', 'massDiff_DNA') sequences = buildSequences(self.sequences, sequenceIdx, sequenceLengths, self._dnaPolymeraseElongationRate()) # Use polymerize algorithm to quickly calculate the number of # elongations each "polymerase" catalyzes reactionLimit = dNtpCounts.sum() result = polymerize(sequences, dNtpCounts, reactionLimit, self.randomState) sequenceElongations = result.sequenceElongation dNtpsUsed = result.monomerUsages # Compute mass increase for each polymerizing chromosome massIncreaseDna = computeMassIncrease( sequences, sequenceElongations, self.polymerized_dntp_weights.asNumber(units.fg)) updatedMass = massDiffDna + massIncreaseDna # Update positions of each "polymerase" updatedLengths = sequenceLengths + sequenceElongations activeDnaPoly.attrIs(sequenceLength=updatedLengths, massDiff_DNA=updatedMass) # Update counts of polymerized metabolites self.dntps.countsDec(dNtpsUsed) self.ppi.countInc(dNtpsUsed.sum()) ## Module 3: replication termination # Determine if any polymerases reached the end of their sequences. If # so, terminate replication and update the attributes of the remaining # polymerases and OriC's to reflect the new chromosome structure. terminalLengths = self.sequenceLengths[sequenceIdx] didTerminate = (updatedLengths == terminalLengths) terminatedChromosomes = np.bincount(sequenceIdx[didTerminate], minlength=self.sequences.shape[0]) # If any of the polymerases were terminated, check if all polymerases # initiated the same round as the terminated polymerases has already # been removed - if they have, update attributes of the remaining # polymerases and oriC's, and remove the polymerases. This is not done # when some polymerases were initiated in the same timestep, as the # attributes for these new polymerases cannot be updated in the same # timestep. if didTerminate.sum() > 0 and replication_initiated == False: # Get attributes from active DNA polymerases and oriC's sequenceIdx, chromosomeIndexPolymerase, replicationRound = activeDnaPoly.attrs( 'sequenceIdx', 'chromosomeIndex', 'replicationRound') chromosomeIndexOriC = oriCs.attr('chromosomeIndex') # Check that all terminated polymerases were initiated in the same # replication round assert np.unique(replicationRound[didTerminate]).size == 1 # Get chromosome indexes of the terminated polymerases chromosomeIndexesTerminated = np.unique( chromosomeIndexPolymerase[didTerminate]) newChromosomeIndex = chromosomeIndexPolymerase.max() + 1 # Get replication round index of the terminated polymerases terminatedRound = replicationRound[didTerminate][0] for chromosomeIndexTerminated in chromosomeIndexesTerminated: # Get all remaining active polymerases initiated in the same # replication round and in the given chromosome replicationRoundMatch = (replicationRound == terminatedRound) chromosomeMatch = ( chromosomeIndexPolymerase == chromosomeIndexTerminated) remainingPolymerases = np.logical_and(replicationRoundMatch, chromosomeMatch) # Get all terminated polymerases in the given chromosome terminatedPolymerases = np.logical_and(didTerminate, chromosomeMatch) # If all active polymerases are terminated polymerases, we are # ready to split the chromosome and update the attributes. if remainingPolymerases.sum() == terminatedPolymerases.sum(): # For each set of polymerases initiated in the same # replication round, update the chromosome indexes to a new # index for half of the polymerases. for roundIdx in np.arange(terminatedRound + 1, replicationRound.max() + 1): replicationRoundMatch = (replicationRound == roundIdx) polymerasesToSplit = np.logical_and( replicationRoundMatch, chromosomeMatch) n_matches = polymerasesToSplit.sum() # Number of polymerases initiated in a single round # must be a multiple of eight. assert n_matches % 8 == 0 # Update the chromosome indexes for half of the polymerases secondHalfIdx = np.where(polymerasesToSplit)[0][( n_matches // 2):] chromosomeIndexPolymerase[ secondHalfIdx] = newChromosomeIndex # Reset chromosomeIndex for active DNA polymerases activeDnaPoly.attrIs( chromosomeIndex=chromosomeIndexPolymerase) # Get oriC's in the chromosome getting divided chromosomeMatchOriC = ( chromosomeIndexOriC == chromosomeIndexTerminated) n_matches = chromosomeMatchOriC.sum() # Number of OriC's in a dividing chromosome should be even assert n_matches % 2 == 0 # Update the chromosome indexes for half of the OriC's secondHalfIdx = np.where(chromosomeMatchOriC)[0][( n_matches // 2):] chromosomeIndexOriC[secondHalfIdx] = newChromosomeIndex # Reset chromosomeIndex for oriC's oriCs.attrIs(chromosomeIndex=chromosomeIndexOriC) # Increment the new chromosome index in case another # chromosome needs to be split newChromosomeIndex += 1 # Delete terminated polymerases activeDnaPoly.delByIndexes(np.where(didTerminate)[0]) # Update counts of newly created chromosome halves. These will be # "stitched" together in the ChromosomeFormation process self.partialChromosomes.countsInc(terminatedChromosomes)
def evolveState(self): ntpCounts = self.ntps.counts() self.writeToListener("GrowthLimits", "ntpAllocated", ntpCounts) activeRnaPolys = self.activeRnaPolys.molecules() if len(activeRnaPolys) == 0: return # Determine sequences that can be elongated rnaIndexes, transcriptLengths, massDiffRna = activeRnaPolys.attrs('rnaIndex', 'transcriptLength', 'massDiff_mRNA') sequences = buildSequences(self.rnaSequences, rnaIndexes, transcriptLengths, self.rnapElngRate) ntpCountInSequence = np.bincount(sequences[sequences != polymerize.PAD_VALUE], minlength = 4) # Polymerize transcripts based on sequences and available nucleotides reactionLimit = ntpCounts.sum() result = polymerize(sequences, ntpCounts, reactionLimit, self.randomState) sequenceElongations = result.sequenceElongation ntpsUsed = result.monomerUsages nElongations = result.nReactions # Calculate changes in mass associated with polymerization and update active polymerases massIncreaseRna = computeMassIncrease(sequences, sequenceElongations, self.ntWeights) updatedMass = massDiffRna + massIncreaseRna didInitialize = (transcriptLengths == 0) & (sequenceElongations > 0) updatedLengths = transcriptLengths + sequenceElongations updatedMass[didInitialize] += self.endWeight activeRnaPolys.attrIs(transcriptLength = updatedLengths, massDiff_mRNA = updatedMass) # Determine if transcript has reached the end of the sequence terminalLengths = self.rnaLengths[rnaIndexes] didTerminate = (updatedLengths == terminalLengths) terminatedRnas = np.bincount(rnaIndexes[didTerminate], minlength = self.rnaSequences.shape[0]) # Remove polymerases that have finished transcription from unique molecules activeRnaPolys.delByIndexes(np.where(didTerminate)[0]) nTerminated = didTerminate.sum() nInitialized = didInitialize.sum() nElongations = ntpsUsed.sum() # Update bulk molecule counts self.ntps.countsDec(ntpsUsed) self.bulkRnas.countsIs(terminatedRnas) self.inactiveRnaPolys.countInc(nTerminated) self.ppi.countInc(nElongations - nInitialized) # Calculate stalls expectedElongations = np.fmin(self.rnapElngRate, terminalLengths - transcriptLengths) rnapStalls = expectedElongations - sequenceElongations # Write outputs to listeners self.writeToListener("TranscriptElongationListener", "countRnaSynthesized", terminatedRnas) self.writeToListener("TranscriptElongationListener", "countNTPsUSed", nElongations) self.writeToListener("GrowthLimits", "ntpUsed", ntpsUsed) self.writeToListener("RnapData", "rnapStalls", rnapStalls) self.writeToListener("RnapData", "ntpCountInSequence", ntpCountInSequence) self.writeToListener("RnapData", "ntpCounts", ntpCounts) self.writeToListener("RnapData", "expectedElongations", expectedElongations.sum()) self.writeToListener("RnapData", "actualElongations", sequenceElongations.sum()) self.writeToListener("RnapData", "didTerminate", didTerminate.sum()) self.writeToListener("RnapData", "terminationLoss", (terminalLengths - transcriptLengths)[didTerminate].sum())
def calculateRequest(self): # Set ribosome elongation rate based on simulation medium environment and elongation rate factor # which is used to create single-cell variability in growth rate # The maximum number of amino acids that can be elongated in a single timestep is set to 22 intentionally as the minimum number of padding values # on the protein sequence matrix is set to 22. If timesteps longer than 1.0s are used, this feature will lead to errors in the effective ribosome # elongation rate. current_nutrients = self._external_states['Environment'].nutrients if self.translationSupply: self.ribosomeElongationRate = np.min( [ self.maxRibosomeElongationRate, int( stochasticRound( self.randomState, self.maxRibosomeElongationRate * self.timeStepSec())) ] ) # Will be set to maxRibosomeElongationRate if timeStepSec > 1.0s else: self.ribosomeElongationRate = np.min([ 22, int( stochasticRound( self.randomState, self.elngRateFactor * self.ribosomeElongationRateDict[current_nutrients]. asNumber(units.aa / units.s) * self.timeStepSec())) ]) # Request all active ribosomes self.activeRibosomes.requestAll() activeRibosomes = self.activeRibosomes.allMolecules() if len(activeRibosomes) == 0: return # Build sequences to request appropriate amount of amino acids to # polymerize for next timestep proteinIndexes, peptideLengths = activeRibosomes.attrs( 'proteinIndex', 'peptideLength') sequences = buildSequences(self.proteinSequences, proteinIndexes, peptideLengths, self.ribosomeElongationRate) sequenceHasAA = (sequences != polymerize.PAD_VALUE) aasInSequences = np.bincount(sequences[sequenceHasAA], minlength=21) if self.translationSupply: translationSupplyRate = self.translation_aa_supply[ current_nutrients] * self.elngRateFactor self.writeToListener("RibosomeData", "translationSupply", translationSupplyRate.asNumber()) dryMass = (self.readFromListener("Mass", "dryMass") * units.fg) molAasRequested = translationSupplyRate * dryMass * self.timeStepSec( ) * units.s countAasRequested = units.convertNoUnitToNumber(molAasRequested * self.nAvogadro) countAasRequested = np.fmin( countAasRequested, aasInSequences ) # Check if this is required. It is a better request but there may be fewer elongations. else: countAasRequested = aasInSequences self.aas.requestIs(countAasRequested) self.writeToListener("GrowthLimits", "aaPoolSize", self.aas.total()) self.writeToListener("GrowthLimits", "aaRequestSize", countAasRequested) # Request GTP for polymerization based on sequences gtpsHydrolyzed = np.int64( np.ceil(self.gtpPerElongation * countAasRequested.sum())) self.writeToListener("GrowthLimits", "gtpPoolSize", self.gtp.total()[0]) self.writeToListener("GrowthLimits", "gtpRequestSize", gtpsHydrolyzed) # GTP hydrolysis is carried out in Metabolism process for growth associated maintenence # THis is set here for metabolism to use self.gtpRequest = gtpsHydrolyzed
def evolveState(self): # Write allocation data to listener self.writeToListener("GrowthLimits", "gtpAllocated", self.gtp.count()) self.writeToListener("GrowthLimits", "aaAllocated", self.aas.counts()) # Get number of active ribosomes activeRibosomes = self.activeRibosomes.molecules() self.writeToListener("GrowthLimits", "activeRibosomeAllocated", len(activeRibosomes)) if len(activeRibosomes) == 0: return # Build amino acids sequences for each ribosome to polymerize proteinIndexes, peptideLengths, massDiffProtein = activeRibosomes.attrs( 'proteinIndex', 'peptideLength', 'massDiff_protein') sequences = buildSequences(self.proteinSequences, proteinIndexes, peptideLengths, self.ribosomeElongationRate) if sequences.size == 0: return # Calculate elongation resource capacity aaCountInSequence = np.bincount( sequences[(sequences != polymerize.PAD_VALUE)]) aaCounts = self.aas.counts() # Using polymerization algorithm elongate each ribosome up to the limits # of amino acids, sequence, and GTP result = polymerize( sequences, aaCounts, 10000000, # Set to a large number, the limit is now taken care of in metabolism self.randomState) sequenceElongations = result.sequenceElongation aasUsed = result.monomerUsages nElongations = result.nReactions # Update masses of ribosomes attached to polymerizing polypeptides massIncreaseProtein = computeMassIncrease(sequences, sequenceElongations, self.aaWeightsIncorporated) updatedLengths = peptideLengths + sequenceElongations didInitialize = ((sequenceElongations > 0) & (peptideLengths == 0)) updatedMass = massDiffProtein + massIncreaseProtein updatedMass[didInitialize] += self.endWeight # Write current average elongation to listener currElongRate = (sequenceElongations.sum() / len(activeRibosomes)) / self.timeStepSec() self.writeToListener("RibosomeData", "effectiveElongationRate", currElongRate) # Update active ribosomes, terminating if neccessary activeRibosomes.attrIs(peptideLength=updatedLengths, massDiff_protein=updatedMass) # Ribosomes that reach the end of their sequences are terminated and # dissociated into 30S and 50S subunits. The polypeptide that they are polymerizing # is converted into a protein in BulkMolecules terminalLengths = self.proteinLengths[proteinIndexes] didTerminate = (updatedLengths == terminalLengths) terminatedProteins = np.bincount( proteinIndexes[didTerminate], minlength=self.proteinSequences.shape[0]) activeRibosomes.delByIndexes(np.where(didTerminate)[0]) self.bulkMonomers.countsInc(terminatedProteins) nTerminated = didTerminate.sum() nInitialized = didInitialize.sum() self.ribosome30S.countInc(nTerminated) self.ribosome50S.countInc(nTerminated) # Update counts of amino acids and water to reflect polymerization reactions self.aas.countsDec(aasUsed) self.h2o.countInc(nElongations - nInitialized) # Report stalling information expectedElongations = np.fmin(self.ribosomeElongationRate, terminalLengths - peptideLengths) ribosomeStalls = expectedElongations - sequenceElongations # Write data to listeners self.writeToListener("GrowthLimits", "aasUsed", aasUsed) self.writeToListener("GrowthLimits", "gtpUsed", self.gtpUsed) self.writeToListener("RibosomeData", "ribosomeStalls", ribosomeStalls) self.writeToListener("RibosomeData", "aaCountInSequence", aaCountInSequence) self.writeToListener("RibosomeData", "aaCounts", aaCounts) self.writeToListener("RibosomeData", "expectedElongations", expectedElongations.sum()) self.writeToListener("RibosomeData", "actualElongations", sequenceElongations.sum()) self.writeToListener( "RibosomeData", "actualElongationHist", np.histogram(sequenceElongations, bins=np.arange(0, 23))[0]) self.writeToListener( "RibosomeData", "elongationsNonTerminatingHist", np.histogram(sequenceElongations[~didTerminate], bins=np.arange(0, 23))[0]) self.writeToListener("RibosomeData", "didTerminate", didTerminate.sum()) self.writeToListener("RibosomeData", "terminationLoss", (terminalLengths - peptideLengths)[didTerminate].sum()) self.writeToListener("RibosomeData", "numTrpATerminated", terminatedProteins[self.trpAIndex]) self.writeToListener("RibosomeData", "processElongationRate", self.ribosomeElongationRate / self.timeStepSec())