Releases: mgcooper/baseflow
Release list
baseflow 1.2.0
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 ha...
v1.1.0
Added
fitabaccepts afitoptsstruct. Each field overrides the
same-named option:weights,order,mask,quantile,
refqtls,Nboot,alpha, orplotfit.fitaberrors withbaseflow:fitab:invalidFitoptfor afitopts
field of the wrong type and withbaseflow:fitab:unknownFitoptfor an
unknown field.fiteventsandsetopts('fitevents')acceptfitoptsand pass it
to everyfitabcall.weightsandmaskmust be scalars there,
because each event fit has its own points. Any other size raises
baseflow:fitevents:nonscalarFitopt.fitabexpands a scalar
weightsormaskto every point.fiteventsandsetopts('fitevents')accept actsmethodoption.
It selects theCTSstencil:B1(the default),B2,F1,F2,
C2, orC4.fiteventspasses it togetdqdt.fiteventsforwards afitorderoption tofitabfor linear
reservoir fits.struct2vararginconverts a name-value struct to a cell array.
trendplotandformatPlotMarkerscall it in place of
namedargs2cell.nonnansegmentsaccepts a vector, a matrix, or a cell array whose
elements are vectors or matrices, and applies the same rules to each
element. For a matrix, theoptioninput selects one result per
column ('each', the default), the rows where every column is non-nan
('all'), or the rows where any column is non-nan ('any').tests/test_eqstrings.mchecks the value and symbolic labels of
aQbString,QtString, andQtauString.fitabfits the'ols'method on GNU Octave. A weighted
least-squares solve with t-based confidence intervals replaces the
Curve Fitting Toolboxfitandconfintcalls, and on MATLAB the
results match them to rounding.toolbox/docs/baseflow_powerlaw_notation.mmaps the exponent
notation ofplfit,r_plfit, the MATLAB generalized Pareto
distribution, and the toolboxbandtauconventions. It runs a
worked comparison on synthetic data.- Vendored helpers
yorkfit,nanmean,nanmedian,tocolumn, and
renametimetabletimevarmake the core workflow chain self-contained. - Example sections in the help of 16 core-workflow functions, and help
text in many private helper functions. - New test suites:
test_fitcts: every stencil against the analytic derivative of an
exponential recession, the midpoint times, both errors, and the
getdqdtpath;test_fitopts: the overrides, both errors, precedence, scalar
expansion, and thefiteventspath;test_peakfinder: empty input, directly and throughislocalmax;test_plfitb_hanel: the arguments the'hanel'method passes to
r_plfit;test_corechain:eventtau,globalfit,fitphi,gpfitb,
fitphidist, andaQbString;test_dependencies: core-chain self-containment, declared
products, the option outputs, the'resolve'file copies, the
plfitbknown-external classification, theloadflowparked
reader, and theSetup('dependencies')report;test_version: every version source agrees.
- The demo scripts run under the suite (
tests/test_demos.m). The
theory demos skip when the Symbolic Math Toolbox is not installed. tests/octave_smoke.m, a plain script with bare asserts, runs the
core workflow in GNU Octave and MATLAB: load the example data, then
getevents,fitevents, andfitabwith'nls'and'ols'.- Plain unit tests of helpers that the toolbox vendors from matfunclib
(nanmean,nanmedian,yorkfit,tocolumn,timetablereduce,
nonnansegments,withcd,listfiles, andmpackagefolders) live
in the matfunclib library test folders, next to the source functions.
The matfunclib sources carry the help and fixes from the toolbox
copies. tests/closenewfigs.mcloses the figures a test opens, so a full
suite run leaves zero open figures and keeps the figures a user had
open.- A MATLAB project definition file.
TODO.mdaudits all work-in-progress signals: sandbox TODO lists,
in-code markers, and disabled code blocks. It records their
provenance, classification, and recommendations.- This changelog and a minimal
CONTRIBUTING.md.
Fixed
- The project
.octavercstarts the toolbox with
addpath(fullfile(pwd, 'toolbox'))andSetup('addpath'). It sourced
Setup.m, which is a function file intoolbox/, so Octave started in
the repository root did not add the toolbox to the path. eventfinderdetects hydrograph troughs again. In 1.0.0 its
Octave-compatibleislocalminandislocalmaxwrappers called
peakfinderwith a threshold of 0, which dropped every minimum of
positive flow and every negative local maximum of dQ/dt. The wrappers
apply no threshold, which restores the v0.1.0 behavior of the MATLAB
islocalmaxandislocalminfunctions, and the vendoredpeakfinder
keeps a single interior peak when endpoints are excluded. Event counts
change: the example data gives 287 events with thesetoptsdefaults
(327 in 1.0.0). The Kuparuk annual workflow gives 230 events and a
global b of 1.3541, which matches the published 1.3540 (1.3519 in
1.0.0).QtStringandQtauStringvalue labels showed an italic "e" with the
latex interpreter and printed the wrong mantissa for a >= 10 (for
example 1250000e^{3} for a = 1250). They build the label the way
aQbStringdoes.- Getting Started lists the
baseflow.setoptsdefaults, which direct
name-value calls also use. It no longer
labelsplotdqdtdeprecated. Theeventfinderhelp and the
setoptsfitevents option list match their parsers. - Continuous integration did not trigger: YAML parsed the
space-separated branch list as one branch named "main dev". The
workflow runs on every push and pull request tomainanddev. - The CI workflow pins its actions and uploads the JUnit results and
Cobertura coverage as artifacts. - Version metadata disagreed: at the 1.0.0 tag,
baseflow.internal.versionandtoolbox/info.xmlreported 0.1.0,
DESCRIPTIONreported 0.1.1, andCITATION.cffreported 1.0.0. +deps/peakfinderreturns empty outputs for empty input. The
empty-input branch assignedvarargout, which left the named outputs
peakIndsandpeakMagsundefined.fitctscomputes theC4stencil as the fourth-order centered
difference. The draft stencil subtracted and addedQ(i+2), which
cancelled the term, and never usedQ(i-2).fitabappliesfitopts. The parser readparser.Unmatched, which
is always empty, andfiteventsdiscarded the option.plfitbmethod'hanel'passes'cdat'tor_plfit, sor_plfit
fits the continuous tau sample and does not bin it on an integer grid.plfitbmethod'hanel'passes the exponent search bounds as
'exp_min'and'exp_max'.r_plfitignores the'alpha_min'and
'alpha_max'names and used its default range of 0 to 5.nonnansegmentsreturns the correct segments for data with leading or
trailing nans. It errored or returned wrong indices for them. It keeps
thenminfilter thateventfinderuses to remove short segments.
For an all-nan vector, it returns empty (0-by-1) start, end, and
length outputs.fitphierrors withbaseflow:fitphi:unsupportedSolutionfor a
solution pair with no derived formula. For example, whenb2is
incompatible with the Rupp and Selker (2005) solution, the non-flat
branch withsoln1'RS05'falls back to the Boussinesq (1903)
late-time solution. The resulting pairRS05_BS03has no formula.
fitphireturned unassigned outputs for such a pair.ccdfreads itsmakeplotoption from the parser results. The option
caused an error on every call.Setup('install')runs the dependency check, which an early return
skipped. Inmatlab -batchruns, it does not prompt before a
re-install.fitabdefaults theqtlmethod's polynomial order to 1.fitvtscomputesdtas a number, not as adurationvalue.struct2vararginassigns default values for its outputs.- Field names in
numfitsandnumeventsmatch the current structures. citing_baseflowcarries the JOSS citation (doi 10.21105/joss.05492).
It states the BSD 3-Clause license apart from the citation request.toolbox/functionSignatures.jsonmatches the parsers. The
derivmethodchoices forfitevents,getdqdt, andsetoptsare
VTS,ETS, andCTS. Thefiteventsandsetoptsentries list
ctsmethodandfitopts. Theglobalfitandsetoptsentries name
drainagedensity.- The BSD 3-Clause text in
+internal/private/withcd.mhad corrupted
characters. It matches the license template. - The suite builder skipped
tests/test_withcd.mbecause the file
declared no test output. That test now lives in matfunclib. test_conversionschecked thebtokconversion against the
reciprocal of the gpfit relation k = (1-b)/(b-2) at b = 1.5, where both
equal 1. It checks b = 1.4 and the inversektobconversion.
Changed
getevents,eventfinder, andwrapeventstake their name-value
defaults frombaseflow.setopts('getevents'), the values that produced
the published results and that the demos use. A direct call without an
options struct usesfmax= 1,rmin= 1,rmnochange= true, and
rmrain= true. In 1.0.0 these parsers usedfmax= 2,rmin= 0,
andrmrain= false (andrmnochange= false ingeteventsand
eventfinder), so direct-call results change.globalfitdefaultsaquiferslopeto 0, thesetoptsvalue.globalfit
does not use this input, so results do not change.geteventsandeventfinderrequirermax> 1. Thermnochange
filter counts each nan as a run of length 1, sormax<= 1 rejected
every day.aQbString,QtString, andQtauStringshare one help layout, input
parser, and label format. Their symbolic labels come from
baseflow.getstring.- The
runlengthand `ismi...
v1.0.0-joss-branch
v1.0.0 Released with JOSS paper.
- Moved user-facing features to toolbox folder
- Retained developer-facing features in top-level repo
- Changed +bfra namespace to +baseflow
- Added Octave compatibility across all demo files and nominally all functions
- Added demo files in .m format for Octave and .mlx for Matlab
- Added searchable doc database
- Added toolbox management functions to +internal
- Moved util to +baseflow/private to clarify main user-facing functions in +baseflow namespace
- Documented API in Getting Started
- Moved tests from +bfra/+test to top-level tests/ folder for compatibility with built-in runtests function
v1.0.0
v1.0.0
- Moved user-facing features to toolbox folder
- Retained developer-facing features in top-level repo
- Changed +bfra namespace to +baseflow
- Added Octave compatibility across all demo files and nominally all functions
- Added demo files in .m format for Octave and .mlx for Matlab
- Added searchable doc database
- Added toolbox management functions to +internal
- Moved util to +baseflow/private to clarify main user-facing functions in +baseflow namespace
- Documented API in Getting Started
- Moved tests from +bfra/+test to top-level tests/ folder for compatibility with built-in runtests function
Note that this release is identical to v1.0.0-joss-branch.
Full Changelog: v0.1.0...v1.0.0