/// <summary> /// Initialise the population with an initial set of cohorts, iterating each one /// every time a new one is added so they are all appropriately "decayed" /// </summary> /// <param name="SpinUpTemps"></param> public unsafe void Initialise(double[] SpinUpTemps) { if (SpinUpTemps.Length != m_LifespanSlices) { throw new ArgumentException("Population must be initialised with one temp for each slice in a lifespan"); } // initialise the population arrays (all values will be set to 0) m_Contribs = new double[m_LifespanSlices]; m_DegDays = new double[m_LifespanSlices]; double[] localContribs = m_Contribs; double[] localDegDays = m_DegDays; for (int i = 0; i < m_LifespanSlices; i++) { double minTemp = SpinUpTemps[i]; double survivalRate = MossieMethods.GetSurvivingFraction(minTemp, m_SliceLengthDays, m_UpperTempLimit); double degreeDays = Math.Max(((minTemp - m_TempThreshold) * m_SliceLengthDays), 0); // decrease all the living cohorts by the survival fraction and // we don't care what the actual result of the sum is at this point for (int cohPos = 0; cohPos < i; cohPos++) { localContribs[cohPos] *= survivalRate; localDegDays[cohPos] += degreeDays; } // "hatch" the next cohort localContribs[i] = 1; localDegDays[i] = 0; } m_Contribs = localContribs; m_DegDays = localDegDays; IsInitialised = true; }
/// <summary> /// Initialise the population with an initial set of cohorts, iterating each one /// every time a new one is added so they are all appropriately "decayed" /// </summary> /// <param name="SpinUpTemps"></param> public void Initialise(double[] SpinUpTemps) { if (SpinUpTemps.Length != m_LifespanSlices) { throw new ArgumentException("Population must be initialised with one temp for each slice in a lifespan"); } m_Contribs = new double[m_LifespanSlices]; m_DegDays = new double[m_LifespanSlices]; double[] localContribs = m_Contribs; double[] localDegDays = m_DegDays; for (int i = 0; i < m_LifespanSlices; i++) { double minTemp = SpinUpTemps[i]; // survival rate is zero if the upper temp threshold is exceeded, meaning all cohorts will die double survivalRate = MossieMethods.GetSurvivingFraction(minTemp, m_SliceLengthDays, m_UpperTempLimit); double degreeDays = Math.Max(((minTemp - m_TempThreshold) * m_SliceLengthDays), 0); // we don't care what the actual result of the sum is at this point for (int cohPos = 0; cohPos < i; cohPos++) { localContribs[cohPos] *= survivalRate; localDegDays[cohPos] += degreeDays; } localContribs[i] = 1; localDegDays[i] = 0; } m_Contribs = localContribs; m_DegDays = localDegDays; IsInitialised = true; }
public double Iterate(double minTemp) { if (!IsInitialised) { throw new InvalidOperationException("Population has not yet been initialised"); } // Calculate degree days once here rather than in every Cohort. double degreeDays = Math.Max(((minTemp - m_TempThreshold) * m_SliceLengthDays), 0); // Calculate survival fraction once here rather than in every Cohort double survivalRate = MossieMethods.GetSurvivingFraction(minTemp, m_SliceLengthDays, m_UpperTempLimit); // Temperature suitability at this slice is simply the sum of Contributions from every Cohort. // This is given before applying the death / decay of this timeslice. // So all we have to do is this time-slice's temperature to all currently living cohorts and // summarise the return values. double tsAtSlice = 0; // It would be sweeter to say: // double tsAtSLice = m_Population.Sum(coh => coh.Iterate(degreeDays, survivalRate)); // But that's substantially slower thanks to the linq overhead, given the tightness of this loop. foreach (Cohort coh in m_Population) { tsAtSlice += coh.Iterate(degreeDays, survivalRate); } // Remove the oldest cohort, which should now be dead anyway Cohort tDeadCohort = m_Population.Dequeue(); //Debug.Assert(tDeadCohort.IsDead); // Add the new cohort m_Population.Enqueue(new Cohort(m_InfectionThreshold, m_LifespanSlices)); return(tsAtSlice); }
/// Initialise the population with an initial set of cohorts, iterating each one /// every time a new one is added so they are all appropriately "decayed" /// </summary> /// <param name="SpinUpTemps"></param> public void Initialise(double[] SpinUpTemps) { if (SpinUpTemps.Length != m_LifespanSlices) { throw new ArgumentException("Population must be initialised with one temp for each slice in a lifespan"); } for (int i = 0; i < m_LifespanSlices; i++) { double minTemp = SpinUpTemps[i]; double survivalRate = MossieMethods.GetSurvivingFraction(minTemp, m_SliceLengthDays, m_UpperTempLimit); double degreeDays = Math.Max(((minTemp - m_TempThreshold) * m_SliceLengthDays), 0); // we don't care what the actual result of the sum is at this point // It's much neater to say: //m_Population.Sum(coh => coh.Iterate(degreeDays, survivalRate)); // But also much slower! Use ye olde loop instead. foreach (Cohort coh in m_Population) { coh.Iterate(degreeDays, survivalRate); } m_Population.Enqueue(new Cohort(m_InfectionThreshold, m_LifespanSlices)); } IsInitialised = true; }
public double Iterate(double minTemp) { if (!IsInitialised) { throw new InvalidOperationException("Population has not yet been initialised"); } // work on a local copy rather than referencing the fields in the tight loop; // this saves approx 10% of overall program runtime! double[] localContribs = m_Contribs; double[] localDegDays = m_DegDays; double localThreshold = m_InfectionThreshold; // Calculate degree days once here rather than in every Cohort. double degreeDays = Math.Max(((minTemp - m_TempThreshold) * m_SliceLengthDays), 0); // Calculate survival fraction once here rather than in every Cohort double survivalRate = MossieMethods.GetSurvivingFraction(minTemp, m_SliceLengthDays, m_UpperTempLimit); // Temperature suitability at this slice is simply the sum of Contributions from every Cohort. // This is given before applying the death / decay of this timeslice. // So all we have to do is apply this time-slice's temperature to all currently living cohorts and // summarise the return values. double tsAtSlice = 0; // calculate and summarise the contribution of all living cohorts first... for (int cohPos = 0; cohPos < localContribs.Length; cohPos++) { if (localDegDays[cohPos] > localThreshold) { tsAtSlice += localContribs[cohPos]; } // ... BEFORE applying the decay function localContribs[cohPos] *= survivalRate; localDegDays[cohPos] += degreeDays; } // ... and BEFORE replacing the oldest dead cohort with a new one localContribs[m_NextToDie] = 1; localDegDays[m_NextToDie] = 0; m_Contribs = localContribs; m_DegDays = localDegDays; m_NextToDie = (m_NextToDie + 1) % m_LifespanSlices; return(tsAtSlice); }
public unsafe double Iterate(double SliceTemp) { if (!IsInitialised) { throw new InvalidOperationException("Population has not yet been initialised"); } // Calculate degree days once here rather than in every Cohort. double degreeDays = (SliceTemp - m_TempThreshold) * m_SliceLengthDays; if (degreeDays < 0) { degreeDays = 0; } // Calculate survival fraction once here rather than in every Cohort. It may be interesting at some stage // to investigate calculating a survival rate that varies with age to reflect different acceptable temp // ranges at different stages of the lifecycle. double survivalRate = MossieMethods.GetSurvivingFraction(SliceTemp, m_SliceLengthDays, m_UpperTempLimit); // Temperature suitability at this slice is simply the sum of Contributions from every Cohort. // This is given before applying the death / decay of this timeslice. // So all we have to do is apply this time-slice's temperature to all currently living cohorts and // summarise the return values. double tsAtSlice = 0; // work on a local copy rather than referencing the fields in the tight loop; // this single step saves approx 10% of overall program runtime in the array-based implementation. double[] localContribs = m_Contribs; double[] localDegDays = m_DegDays; double localThreshold = m_InfectionThreshold; int count = m_LifespanSlices; fixed(double *pContribs = localContribs, pDegDays = localDegDays) { double *pContrib = pContribs, pDegDay = pDegDays; for (int i = 0; i < count; i++) { // go through each cohort of the current population, based on pointers if (*pDegDay > localThreshold) { // this cohort has reached sporogenesis threshold, it contributes to the temperature suitability // of this timeslice based on the surviving fraction _before_ this timeslice's die-off tsAtSlice += *pContrib; } // reduce the surviving fraction of this cohort (possibly to zero if mosoquito-baking temp was exceeded) *pContrib *= survivalRate; // add the degree-days of this slice to the total degree days experienced by this cohort (probably quicker // just to do it rather than only conditionally doing it if baking didn't occur) *pDegDay += degreeDays; // move pointer for contribution and degree day onto next value pContrib++; pDegDay++; } } // spawn a new one (replacing the previous value at this position, the cohort which is now dead) localContribs[m_NextToDie] = 1; localDegDays[m_NextToDie] = 0; // copy local variables back to field m_Contribs = localContribs; m_DegDays = localDegDays; m_NextToDie = (m_NextToDie + 1) % m_LifespanSlices; return(tsAtSlice); }