food_chain/ecology/resources.js
2026-09-29 23:45:12 +09:00

32 lines
2.1 KiB
JavaScript

import {clamp} from './units.js?v=45';
export class ResourceField{
constructor({id,role,cellCount,energyDensityKJPerKg,representation='field'}){this.id=id;this.role=role;this.energyDensityKJPerKg=energyDensityKJPerKg;this.representation=representation;this.biomass=new Float32Array(cellCount)}
consume(index,amountKg,cellAreaM2){const available=this.biomass[index]*cellAreaM2,taken=Math.min(Math.max(0,amountKg),available);this.biomass[index]=Math.max(0,this.biomass[index]-taken/cellAreaM2);return taken}
}
export const phytoplanktonMuMax = temperatureC => 0.81 * Math.exp(0.0631 * temperatureC);
export function producerCapacity(profile,{moisture=1,shade=0,blocked=false}={}){
if(blocked)return 0;
if(profile.type==='terrestrial')return profile.producer.carryingCapacityKgPerM2 * clamp(0.35 + 0.65*moisture,0,1) * (1 - 0.18*clamp(shade,0,1));
return profile.producer.carryingCapacityKgPerM2;
}
export function producerGrowthPerDay(profile,temperatureC,{moisture=1,light,nutrientN,nutrientP}={}){
if(profile.type==='terrestrial'){
const fT=Math.exp(-0.5*((temperatureC-20)/13)**2),fW=clamp(moisture,0,1),fL=clamp(light ?? profile.environment.light,0,1);
return profile.producer.rMaxPerDay * fT * fW * fL;
}
const fI=clamp(light ?? profile.environment.light,0,1),fN=clamp(nutrientN ?? profile.environment.nutrientN,0,1),fP=clamp(nutrientP ?? profile.environment.nutrientP,0,1);
return phytoplanktonMuMax(temperatureC) * Math.min(fN,fP) * fI;
}
export function updateProducerBiomass(biomass,capacity,growthPerDay,consumedKgPerM2,dtDays){
if(capacity<=0)return 0;const growth=growthPerDay*biomass*(1-biomass/capacity);return clamp(biomass + growth*dtDays - Math.max(0,consumedKgPerM2),0,capacity*1.05);
}
export function updateZooplanktonCohort(producerKgPerM2,zooplanktonKgPerM2,dtDays=1){
const z=Math.max(0,zooplanktonKgPerM2),p=Math.max(0,producerKgPerM2),ingestion=Math.min(p,.9*z*p/(.035+p))*dtDays,assimilated=ingestion*.45,mortality=.10*z*dtDays;
return {producerConsumedKgPerM2:ingestion,nextZooplanktonKgPerM2:Math.max(0,z+assimilated-mortality),productionKgPerM2:Math.max(0,assimilated-mortality)};
}