Aquifer characterization from streamflow measurements by baseflow recession analysis.
This release corrects uncertainty propagation, repairs the point-cloud figures, and makes the core analysis chain run on GNU Octave. It has two breaking changes: the string helpers each return one label, and the vendored baseflow.deps.arrow is removed.
Verified on MATLAB R2025b (453 tests passed) and GNU Octave 11.3.0 with the statistics package 1.9.1.
Added
plotreflineacceptslabelcolor,labelfontsize, andlabelstyle.
labelcolordefaults to the line color,labelfontsizeto 10, and
labelstyleselects'arrow'or'line'for the late-time,
early-time, and user-fit labels.'line'writes the label along the
line, as the upper-envelope label does.plotdqdtacceptslabelcolorandlabelfontsizefor its own labels,
and areflinesoption that selects the reference lines it draws, with
the same valuespointcloudplottakes.plotdqdtandpointcloudplotacceptlabelstyle, whichplotrefline
takes:'arrow', the default, points an arrow at each labeled
reference line, and'line'writes the label along the line. The
'line'style draws no arrow, so it labels the lines on Octave.pointcloudplotacceptslabelcolorandlabelfontsizeand passes
both toplotrefline.plotdqdtandpointcloudplotacceptfontsizefor the axes, which
sets the tick labels and the axis labels, andlegendfontsizefor the
legend. Both default to 12.pointcloudplotandplotdqdtaccept anaxislimitsoption:'snap'
(the default) rounds a limit out to its decade when the decade is
within 0.25 decades,'decades'always rounds out, and'none'keeps
the data limits.- The private helpers
snaploglims,labelanchor,islabelcolor,
islinehandle,labelrefline,breflinetext,sizefigure,
relayoutloglogtext,drawarrow, andaxespixelbox. cloudphiaccepts aplotfitoption (default true). Withplotfit
false it computes phi and draws no figure.fitphidistreturns the phi standard error ash.sefor the 'cdf'
plot type, andphifitensemblereturns it asPhiFit.se.h.pmand
PhiFit.pmkeep the 95% half-width.tools/m2htmlholds a copy of M2HTML (rochefort-lab/m2html at
3821fb8, GPL-2.0-or-later) for the docs build.
tools/m2html/VENDORED.mdrecords its source, license, local changes,
and refresh steps.CONTRIBUTING.mdexplains how to build the docs.
Changed
-
aQbString,QtString, andQtauStringeach return one label.
aQbStringreturns the -dQ/dt = aQ^b label and has noQ0input.
CallQtStringfor the Q(t) label. -
checkeventhas noaxoption. It always opens its own figure. -
dndtuncertaintycombines standard errors and multiplies the result
by the coverage factornorminv(1-alpha/2), so it accepts anyalpha
in (0, 1). Each input term is one standard error: the bootstrap
standard error for phi, theGlobalFitbootstrap bounds, and the
standard error of the regression. On Octave it returns that regression
term after scaling, as it did in 1.1.0. The 1.1.0 terms mixed two levels.- The phi and regression terms were 95% half-widths.
- The tau and b terms were standard errors.
alpha0.32 halved every term except the regression term, which
stayed at 95%.
With
bootfitfalse on the example data,sig_dndtat the default
alphachanges by 0.02%. Its help documentsalphaandtestflag. -
plfitbwarns withbaseflow:plfitb:tauPoleReplicateswhen a
bootstrap replicate has alpha at or below 2. tau has its pole at
alpha 2 and is negative below it, so such a replicate makestau_sig,
tau_L, andtau_Hmeaningless. -
getdqdtwithplotfitstrue draws the event figure for
pickmethod'none', its default, and for afitmethodother than
'none'. -
DESCRIPTIONgives the contact address matt@sierracrestanalytics.com. -
.gitattributesgives.mfiles the ruletext eol=lf. This release
converts the 14.mfiles that used CRLF. A checkout writes LF for
every.mfile. -
DESCRIPTIONrequires the Octavestatisticspackage 1.9.1 or later.
trendplotanddndtuncertaintyuse itsfitlmmodel object. -
baseflow.internal.makedocs('functions')builds the function pages
with the M2HTML copy intools/m2htmland needs no separate M2HTML
install.makedocsrestores the MATLAB path when it returns. -
The demo scripts and live scripts do not call
close all, so a demo
keeps the figures a user has open.makedocs('demos')deletes the
figures each demo export opens. -
hyetographopens a new figure unless an axes handle is passed, so it
does not resize or redraw a figure the user has open. With an axes
handle, it draws in that axes. -
The Getting Started guide, the citing page, and the function
documentation template give the contact address
matt@sierracrestanalytics.com.
Removed
- The vendored
+deps/arrow. Every figure that drew an arrow now calls
the privatedrawarrow, which draws the same arrow, runs on Octave,
and stays correct when the plot-box aspect ratio is manual. A caller
ofbaseflow.deps.arrowhas no replacement in the toolbox: it was a
copy of the File Exchange ARROW by Erik A. Johnson, which is still
available there. - The private helpers
fitlm_octmatandpredictlm.dndtuncertainty
callsfitlmandcoefCI, which MATLAB and the Octavestatistics
package both provide.
Fixed
plotdqdtlabels its late-time lineb = 1, the value of theblate
reference slope. It labeled that line as an estimate, with b-hat beside
the value and two decimals, which names the fitted line of the point
cloud. The new private helperbreflinetextwrites the label of both
functions, so a reference slope reads the same in each.pointcloudplotwrites its legend in latex on MATLAB, so the legend of
the point cloud and the legend of the fit plot set Q and t alike.
Octave has no latex text interpreter, so it keeps the tex form.gpfitb,plplotbandfitphidistdraw their arrows with
drawarrow. The vendored arrow drew a point at or below zero on a log
axis at that value reflected through the origin, because it took the
real part of its complex logarithm.gpfitbreaches that case with a
negative tauExp, which the tail of a power law below alpha 2 gives it,
so the label pointed at a place the data never reaches.drawarrow
draws no arrow there and warns with
baseflow:drawarrow:nonpositiveLogCoordinate.plotreflineandplotdqdtdraw the arrow of a reference-line label
in the new private helperdrawarrow, which builds the head from the
drawn plot box and needs no MATLAB-only axes property. The vendored
+deps/arrowreads the undocumentedWarpToFill, and an axes with a
manual plot-box aspect ratio, whichaxis squaresets inplotdqdt,
turns that property off and sends the vendored function into a branch
its own comments call untested. The shaft then spanned the whole axis.
The arrow keeps the head size and angle it had, and it draws on Octave,
so the'arrow'label style works in both languages.axespixelbox
reads the drawn box fordrawarrowand forloglogangle.plotdqdtlabels its reference lines with the arrowpointcloudplot
draws, in the new private helperlabelrefline. It drew its own arrow,
which scaled its length by the factor that raises the label anchor, so
an arrow could span the whole x range, and its head could stop left of
the axes instead of on the line. The arrow of both figures now spans a
twenty-fifth of the drawn x decades and points at the line.pointcloudplotandplotdqdtreset the angle of a label written
along a line after they set the final axis limits, in the new private
helperrelayoutloglogtext. The angle of a line on a log-log plot
follows the limits, and a MATLAB listener keeps it current, but Octave
has no such event, so an envelope that raised the y limit left the
Octave label off its line.plotreflinewrites its labels in tex on Octave, which has no latex
text interpreter. It asked for latex, so a point cloud drawn on Octave
withreflabelstrue showed the math delimiters.functionSignatures.jsonlists thelabelstyle,labelcolor,
labelfontsize,fontsize, andlegendfontsizeoptions, so name-value
completion offers them. Its two+baseflow/private/subtightentries are
one entry that names every option the function parses.plotreflinestarts a label a twentieth of the x decades inside the
left limit, inlabelanchor. A label of a line that reaches the anchor
height near the left limit sat against the y axis.pointcloudplotandplotdqdtopen a figure where the window manager
puts it. They pinned the figure to [0 0] and [1 1], the bottom-left
corner of the screen, where the dock covers the axis labels. Both take
their size, 640 by 600 points, from the new private helper
sizefigure, so the axis labels fit.pointcloudplotandplotdqdtset the axis ticks after the final axis
limits, so every decade inside the limits carries a tick.pointcloudplotpassesprecisionandtimestepto its envelope
lines. The envelope intercept ignored both, so it always described a
one-day timestep and a precision of one.pointcloudplotdraws in the axes a caller supplies: it keeps the size
of the parent figure, andsetlogtickshandles an axis whose data
reach zero.plotdqdtdraws in its own axes. It drew through the current axes, so
a caller with another axes current split the figure.plotdqdtandpointcloudplotlist rain in their legends. The
plotdqdtguard tested for an axes, and both guards testedisobject,
which is false for the numeric handle Octave returns fromplot, so
the rain entry never appeared on Octave. The new private helper
islinehandletakes the handle of either language.pointcloudplot
gives rain one entry for its several circles, and keeps the entry of
each reference line it names, for areflinesrow or column.plotdqdtruns on Octave, sogetdqdtwithplotfitstrue draws its
event figure there. Its input parser askedvalidateattributesfor the
scalartextattribute, which Octave does not define, and it read the
marker size of the plotted line with dot indexing, which a numeric
handle does not take.plotreflineandplotdqdtgive their labels an explicit color and
font size, so a figure theme does not recolor them and a large axes
font does not enlarge them. The label font size falls from 13 and 11
points to 10.plotreflineraises a label anchor that falls left of the axes, in the
shared private helperlabelanchor. A late-time label drew its arrow
across the left spine and its text over the leftmost markers.plotreflineplaces the upper-envelope label on the line. The label
sat at2*x, which ignores the intercepta = 2/timestep, so it
drifted from the line for any timestep other than one day.private/rotatedLogLogTextdraws its label at the angle of the line.
It had three defects. It read the axes offsets where it needed the
axes size. It applied the slope outside the arctangent. It worked in
figure-normalized units. No single value of itsrtxtfactor served
every layout. The new private helperlogloganglecomputes the angle
from the axes size in pixels and the axis limits. In a live MATLAB
figure, a listener keeps the angle correct after a resize or a limit
change. Octave installs no
listener, and a figure saved withsavefigkeeps its saved angle.
rotatedLogLogTexttakes the axes and the slope in place ofrtxt.getdqdton MATLAB keeps the random stream of its caller.plotdqdt
fits the point cloud to draw its line. A 'qtl' fit bootstraps, so
getdqdtsaves and restores the stream around the plot call.+deps/plvarkeeps the random seed a caller sets. It called
rng('shuffle')on its first call in a session, so a seeded script
could not reproduce its bootstrap uncertainties.dndtuncertaintypropagates the uncertainty of b to N* = 1/(4-2b)
with the derivative 2/(4-2b)^2, in the new private helper
nstaruncertainty. The plain factor 2 holds only for b = 3/2. The
1.1.0 term was (4-2b)^2 times too large, which is 1.7 at the Kuparuk
global b of 1.3541. The term is zero whenglobalfitruns with
bootfitfalse.dndtuncertaintyholds the dq/dt trend uncorrelated with the
event-scale variables. Its column is constant, andcorrreturns nan
for a constant column.fiteventspassesplotfitstogetdqdt, which draws one figure per
event for afitmethodother than 'none'.fiteventsparsed the
option and ignored it.plotreflinedraws its reference line and its label in the axes a
caller supplies. It gives the caller back its current figure and
current axes. On MATLAB the arrow of the late-time, early-time, and
user-fit labels goes to the same axes. Octave draws no arrow and no
label text for those three labels.checkeventruns without an input parser error. The parser gives the
Qandqinputs distinct names. The Octave parser compares names
without case, and the MATLAB parser does so by default.checkeventdraws an event that has no valid flow.globalfitwithplotfitsfalse opens no figure.fitphidistwithshowfitfalse leaves no hidden figure open, so
phifitensemble,dndtuncertainty, andglobalfitdo not accumulate
figures. Its'probplot'plot type runs without an input error and
followsshowfit. Its help documents'probplot'andshowfit.plotaquifertrendplots the GRACE period without a legend error and
returns the third trend handle astrendplot3. Its help documents the
GRACE input.trendplotanddndtuncertaintyrun on Octave, so the Kuparuk demo
runs on Octave.- The
fiteventshelp example runs as written. private/siUnitsToTexwraps each negative exponent once, so labels
such as 'm3 d-1' inhyetographshow the correct superscript.private/fillnansfills only interior nan runs of lengthfmaxor
less and accepts a row vector.private/smoothnoisekeeps each year together on the 'annual' path
and accepts a call with no method input.private/setrainnanaccepts vector input.private/preparecalendarassignstimestepfor every calendar.private/fitctsreturns nan for a single sample.private/runlengthandprivate/anomalyaccept row vectors.private/getplotdataskips axes children that have noXData.private/formatPlotMarkerscallsroundwith one input, which Octave
requires.fitabandloadflowcall the shared private helpers in place of
local copies.- The GitHub Actions workflow uses action versions that run on Node.js 24.
- The GitHub Actions
Testsworkflow runs the test suite. With the
project filebfra.prjat the repository root,matlab-actions/run-tests
selected zero tests, and the runs in 1.1.0 passed without running a
test. The workflow builds the suite fromtests/and fails when the
suite is empty. - The
cloudphiandfitphihelp describedispfitcorrectly: it
prints each phi value. - The Getting Started function list shows dQ/dt as italic text, not as
oversized equation images, and its author email is one mailto link. makedocs('demos')does not overwrite the Octave m-files in
demos/mfiles, and the demo pages show the output of the current code.- The m2html dependency graph matches the current functions.