second
This commit is contained in:
parent
5f7fcb96ed
commit
01204dfa2f
34 changed files with 1498 additions and 0 deletions
40
ecology/demography.js
Normal file
40
ecology/demography.js
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
import {annualSurvivalToDailyHazard,rateToProbability,clamp} from './units.js';
|
||||
|
||||
// Life-history fallbacks are simulator calibration values, not universal biological constants.
|
||||
export const ROLE_DEMOGRAPHY = Object.freeze({
|
||||
'primary-consumer': {annualSurvival:.38,offspringPerEvent:1,reproductionIntervalDays:180},
|
||||
'secondary-consumer': {annualSurvival:.58,offspringPerEvent:1,reproductionIntervalDays:240},
|
||||
'tertiary-consumer': {annualSurvival:.70,offspringPerEvent:1,reproductionIntervalDays:365},
|
||||
detritivore: {annualSurvival:.45,offspringPerEvent:1,reproductionIntervalDays:210},
|
||||
});
|
||||
export function backgroundHazardPerDay(role){return annualSurvivalToDailyHazard((ROLE_DEMOGRAPHY[role]||ROLE_DEMOGRAPHY['primary-consumer']).annualSurvival)}
|
||||
export function mortalityOccurs(hazardPerDay,dtDays,random){return random()<rateToProbability(hazardPerDay,dtDays)}
|
||||
export function reproductionConfig(role){return ROLE_DEMOGRAPHY[role]||ROLE_DEMOGRAPHY['primary-consumer']}
|
||||
export function inBreedingSeason(day,role){
|
||||
const doy=((day%365)+365)%365;
|
||||
if(role==='tertiary-consumer')return doy>=60&&doy<=180;
|
||||
if(role==='secondary-consumer')return (doy>=60&&doy<=165)||(doy>=245&&doy<=300);
|
||||
if(role==='detritivore')return (doy>=35&&doy<=175)||(doy>=220&&doy<=315);
|
||||
return (doy>=45&&doy<=170)||(doy>=225&&doy<=305);
|
||||
}
|
||||
export function offspringEnergyShare(role){return role==='tertiary-consumer'?.18:.13}
|
||||
export function reproductionThreshold(role){return role==='tertiary-consumer'?.82:role==='secondary-consumer'?.8:.78}
|
||||
export const energyFraction = (energy,maxEnergy)=>clamp(maxEnergy>0?energy/maxEnergy:0,0,1);
|
||||
|
||||
|
||||
// Structural growth is paid from assimilated energy. These are simulator
|
||||
// calibration values rather than universal life-history constants.
|
||||
export const GROWTH_ENERGY_COST_KJ_PER_KG = 6000;
|
||||
export const RESERVE_FLOOR_FRACTION_FOR_GROWTH = 0.50;
|
||||
export function initialStructuralMassKg(adultMassKg,offspringSize,ageDays,maturityAgeDays){
|
||||
const adult=Math.max(1e-6,adultMassKg),birth=adult*clamp(offspringSize,0.01,1),progress=clamp(ageDays/Math.max(1e-6,maturityAgeDays),0,1);
|
||||
return birth+(adult-birth)*progress;
|
||||
}
|
||||
export function energyLimitedGrowth({structuralMassKg,adultMassKg,energyKJ,reserveCapacityKJPerKg=1200,ageDays,maturityAgeDays,dtDays}){
|
||||
const mass=Math.max(1e-6,structuralMassKg),adult=Math.max(mass,adultMassKg),remaining=Math.max(0,adult-mass);
|
||||
if(!remaining)return {structuralMassKg:mass,energyKJ,gainKg:0};
|
||||
const reserveFloor=mass*reserveCapacityKJPerKg*RESERVE_FLOOR_FRACTION_FOR_GROWTH,available=Math.max(0,energyKJ-reserveFloor);
|
||||
if(!available)return {structuralMassKg:mass,energyKJ,gainKg:0};
|
||||
const daysToMaturity=Math.max(1,maturityAgeDays-ageDays),potential=remaining*Math.min(1,dtDays/daysToMaturity),affordable=available/GROWTH_ENERGY_COST_KJ_PER_KG,gain=Math.min(remaining,potential,affordable);
|
||||
return {structuralMassKg:mass+gain,energyKJ:energyKJ-gain*GROWTH_ENERGY_COST_KJ_PER_KG,gainKg:gain};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue