class dynamicalSystem { // ----------------------------------------------------------- String name,shortname; String timeUnits,varUnits; int length, nvars, nfluxes, nparams; flux[] fluxes; statevar[] vars; freeParam[] params; float[][] varhistory,fluxhistory,paramhistory; float[] historytime; int nhistend = -1; boolean wrapHistory = false; float tcurrent = 0; float dtlast = nan; float modelTimePerSecond = 1; float tolerance = 1e-4; boolean adjustFluxesAtMaxMin = false; // true = adjust fluxes so statevars don't exceed their max/mins; // false = constrain statevars but pretend full fluxes were applied float dot_stdsize = 0.2; // relative to diagram width float dot_minsize = 0.01; float dot_rigidity = 1; // how linear the scaling is: >> 1, hard to change dot size; << 1, close to linear float line_stdwidth = 4; float line_minwidth = 0.25; float line_maxwidth = 10; float line_rigidity = 1; int arcResolution = 40; boolean showFluxes = true; boolean showDetails = false; boolean showDetailsButton = false; boolean showVarnames = false; boolean showHistory = true; boolean showNewtork = true; boolean showVarDiagnostics = false; int[] networkRect = new int[4]; int[] historyRect = new int[4]; void allocate(int N, int maxvars, int maxfluxes, int maxparams) { length = N; fluxes = new flux[maxfluxes]; nfluxes = 0; vars = new statevar[maxvars]; nvars = 0; params = new freeParam[maxparams]; nparams = 0; varhistory = new float[maxvars][N]; //fluxhistory = new float[maxfluxes][N]; // fluxhistory commented out here and in savetohistory(), so that the amount of // history storage goes like (nvars), not (nvars^2) paramhistory = new float[maxparams][N]; historytime = new float[N]; } dynamicalSystem clone() { println("warning: no clone() method has been defined for this system"); return null; } int addFlux(String thename, String theshortname, int thefrom, int theto, color thecolor, float curv) { flux newflux; newflux = new flux(length,nan); newflux.define(thename, theshortname, thefrom, theto, thecolor, curv); nfluxes++; fluxes[nfluxes-1] = newflux; return nfluxes-1; } int addVar(String thename, String theshortname, float initialval, color thecolor, float theX, float theY) { statevar newvar; newvar = new statevar(length,nan); newvar.define(thename, theshortname, initialval, thecolor, theX, theY); nvars++; vars[nvars-1] = newvar; return nvars-1; } int addParam(String thename, String theshortname, float initialval, float minval, float maxval, color thecolor) { freeParam newparam; newparam = new freeParam(length,nan); newparam.define(thename, theshortname, initialval, minval, maxval, thecolor); nparams++; params[nparams-1] = newparam; return nparams-1; } int addEndpoint(String thename, String theshortname, float theX, float theY) { // special kind of statevar statevar newvar; newvar = new statevar(length,nan); newvar.define(thename, theshortname, nan, nan, theX, theY); nvars++; vars[nvars-1] = newvar; vars[nvars-1].constant = true; vars[nvars-1].display = false; return nvars-1; } int addConstant(String theshortname, float val) { return addConstant(theshortname,theshortname,val); } int addConstant(String thename, String theshortname, float initialval, float minval, float maxval, color thecolor) { // if you addConstant with the arguments you'd use for addParam, the extra ones are ignored return addConstant(thename, theshortname, initialval); } int addConstant(String thename, String theshortname, float val) { // special kind of param int id = addParam(thename,theshortname,val,val,val,color(0)); params[id].frozen = true; return id; } int find(String sn) { // returns the index into fluxes, vars, or params that matches the shortname "sn". // warning: the first match is returned; there's no checking for duplicates. So make sure there // aren't any repetitions of shortnames among the 3 arrays. int a = -1; int i = -1; while ((a==-1) && (i 0) { dV = min(dV, vars[jto].max - Vout[jto]); dV = min(dV, Vout[jfrom] - vars[jfrom].min); } else { dV = max(dV, vars[jto].min - Vout[jto]); dV = max(dV, Vout[jfrom] - vars[jfrom].max); } dV = constrain(dV, fluxes[i].min * dt, fluxes[i].max * dt); } if (!vars[jto].constant) { Vout[jto] += dV; Vout[jto] = constrain(Vout[jto], vars[jto].min, vars[jto].max); } if (!vars[jfrom].constant) { Vout[jfrom] -= dV; Vout[jfrom] = constrain(Vout[jfrom], vars[jfrom].min, vars[jfrom].max); } } return Vout; } // these are needed to make romsBioSlice work. Not elegant. void setInitialConditions() {} void setInitialConditions(float someInputVal) {} float totalP() {return 0;} float totalNitrogen() {return 0;} void adjustments_after_calc_step() { // placeholder for things like recalculating total nitrogen and statevar scales } float[] CALCFLUXES(float[] vslice) { // placeholder for the routine where the system-specific ODEs go return null; } int calc(float DT) { // integrates the system from tcurrent to tcurrent+DT. float[] Vcurrent,Pcurrent,fslice; fslice = new float[nfluxes]; Vcurrent = new float[nvars]; Pcurrent = new float[nparams]; float[] Vfull,Vhalf; Vfull = new float[nvars]; Vhalf = new float[nvars]; float err,thiserr,DTsofar,dt; int errorMsg = 0; for (int i=0; ierr) { err = thiserr; worstVar = i; } } if ((err <= tolerance) || (dt < tolerance*DT)) { // successful // (or dt is so small we'd better call it successful and move on) DTsofar = DTsofar + dt; Vcurrent = Vhalf; // note: haven't saved it to system.vars yet if (err>tolerance) { errorMsg = 1; // if (debug) {println(shortname + ": " + vars[worstVar].shortname + ": half full scale = " + Vhalf[worstVar] + " " + Vfull[worstVar] + " " + vars[worstVar].scale);} } dt = 1.2 * dt; // try a bigger step next time } else { // unsucessful dt = 0.5 * dt; // cut the stepsize in half and try again } } tcurrent = tcurrent + DT; for (int i=0; i 240) {stroke(transparency(dkGrayColor,0.5)); strokeWeight(1);} ellipseMode(CENTER); ellipse(xcen,ycen,2*r,2*r); } void drawonevarlabel(int i, float xcen, float ycen, float r) { color col = vars[i].currentcolor; textAlign(CENTER); if (r>smallfontsize) { setFont("flexi"); fill(lighten(lighten(lighten(col)))); if (brightness(col) > 240) {fill(dkGrayColor);} textSize(r); textVAlign(vars[i].shortname,xcen,ycen,r,"middle"); } else { setFont("small"); // textVAlign(vars[i].shortname,xcen,ycen-r-1,smallfontsize,"bottom"); float theta = atan2(vars[i].yc,vars[i].xc); textVAlign(vars[i].shortname,xcen+(vars[i].radius+smallfontsize)*cos(theta),ycen-(vars[i].radius+smallfontsize)*sin(theta),smallfontsize,"middle"); } } void drawonefluxarrow(int i, float xfrom, float yfrom, float xto, float yto) { strokeWeight(fluxes[i].width); stroke(fluxes[i].currentcolor); float[][] vertices = flux_arc(xfrom,yfrom,xto,yto,fluxes[i].curvature,arcResolution); noStroke(); fill(fluxes[i].thecolor); if (fluxes[i].current>0) { arrowhead(vertices,fluxes[i].arrowheadpos,fluxes[i].width*6,1); } else if (fluxes[i].current<0) { arrowhead(vertices,1-fluxes[i].arrowheadpos,fluxes[i].width*6,-1); } } void draw_history_setup(int n0, int n1) { float xscale,yscale,xoffset,yoffset; float timePrecision = 10; int n; fill(lighten(ltGrayColor)); noStroke(); rect(historyRect[0], historyRect[1], historyRect[2], historyRect[3]); strokeWeight(1.5); stroke(dkGrayColor); noFill(); beginShape(); vertex(historyRect[0],historyRect[1]); vertex(historyRect[0],historyRect[1]+historyRect[3]); vertex(historyRect[0]+historyRect[2],historyRect[1]+historyRect[3]); endShape(); int thetextsize = medfontsize; setFont("med"); fill(dkGrayColor); noStroke(); float t = (float)round(historytime[n0]*timePrecision)/timePrecision; textAlign(CENTER); textVAlign(str(t), historyRect[0], historyRect[1]+historyRect[3]+4, thetextsize, "top"); t = (float)round(historytime[n1]*timePrecision)/timePrecision; textAlign(RIGHT); textVAlign(str(t), historyRect[0]+historyRect[2], historyRect[1]+historyRect[3]+4, thetextsize, "top"); textAlign(LEFT); textVAlign(" " + timeUnits, historyRect[0]+historyRect[2], historyRect[1]+historyRect[3]+4, thetextsize, "top"); textAlign(RIGHT); float total = 0; for (int i=0; i= networkRect[0]) && (mouseX <= networkRect[0]+networkRect[2])) && ((mouseY >= networkRect[1]) && (mouseY <= networkRect[1]+networkRect[3]))); } } // end of class dynamicalSystem // ----------------------------------------------------------------------------------- // dynamical variables: state vars, fluxes, free params // ----------------------------------------------------------------------------------- class flux { int from, to; // indices of statevars; -1 = to/from nothingness float initial, current,scale,max,min; String name, shortname; color thecolor, currentcolor; float curvature, width, arrowheadpos; boolean display; flux(int N, int fillvalue) { display = false; } void define(String thename, String theshortname, int thefrom, int theto, color colr, float curv) { name = thename; shortname = theshortname; from = thefrom; to = theto; initial = 0; current = 0; thecolor = colr; curvature = curv/180*PI; arrowheadpos = 0.75; display = true; scale = 1; min = 0; max = big; } } class statevar { float initial,current,scale,max,min; String name, shortname; float xc,yc,radius; color thecolor, currentcolor; boolean constant,display; float sinkingRate = 0; // used by coupledBiophysicalModel; sinking is handled differently in the NNPZD and allo-upwelling1D models written before v1.3.re statevar(int N, int fillvalue) { constant = true; display = true; } void define(String thename, String theshortname, float initialval, color colr, float theX, float theY) { name = thename; shortname = theshortname; initial = initialval; current = initialval; constant = false; thecolor = colr; xc = theX; yc = theY; scale = 1; min = 0; max = big; } } class freeParam { float initial,min,max,current,previous; String name, shortname; color thecolor; slider controller; boolean controllerOn, frozen; freeParam(int N, int fillvalue) { frozen = false; } void define(String thename, String theshortname, float initialval, float minval, float maxval, color colr) { name = thename; shortname = theshortname; min = minval; max = maxval; controllerOn = false; // no slider by default initial = initialval; current = initialval; previous = initialval; thecolor = colr; } void addController(int[] screenRect) { controllerOn = true; controller = new slider(screenRect[0],screenRect[1],screenRect[2],screenRect[3],1); controller.addName(name); controller.highlightColor = thecolor; } void update() { // sync with controller & put result in _current_ if (controllerOn) { if (frozen) {controller.freeze();} else {controller.unfreeze();} controller.setPos((current-min)/(max-min)); controller.addDisplayVal(current,2); controller.update(); current = controller.getPos() * (max-min) + min; } } void onMousePress() {} }