<- Previous: Getting Started | Back to Index | Next: Input Data Frame ->
In RMC-BestFit, a model is any mathematical representation of a physical process that can be fitted to data. Models define the relationship between parameters and observations, specify prior distributions for Bayesian inference, and compute likelihood functions that measure how well parameter values explain the data.
This chapter introduces the model architecture in RMC-BestFit, describes the core interfaces that all models implement, surveys the pre-built models available in the library, and demonstrates how to create custom models for specialized applications.
Statistical analysis begins with a model — an assumption about how nature generates the data we observe. A flood frequency analyst might assume annual peak flows follow a Log-Pearson Type III distribution. A rating curve developer assumes discharge is a power function of stage. A regional hydrologist might assume extreme value parameters vary smoothly with elevation and distance from the coast.
The choice of model determines:
- What questions we can answer: A stationary model cannot detect trends; a univariate model cannot capture dependence between variables
- What parameters we estimate: Each model has its own set of interpretable parameters
- How uncertainty propagates: The model structure determines how parameter uncertainty translates to predictive uncertainty
- What data are required: Complex models need more data to estimate reliably
RMC-BestFit provides a rich library of pre-built models for common hydrologic applications, along with a flexible framework for creating custom models when the built-in options don't fit your needs.
All models in RMC-BestFit implement the IModel interface, which defines the contract for Bayesian inference:
public interface IModel
{
/// <summary>
/// Gets the list of model parameters with their prior distributions.
/// </summary>
List<ModelParameter> Parameters { get; }
/// <summary>
/// Computes the log-posterior: log-likelihood plus log-prior.
/// </summary>
double LogLikelihood(double[] parameters);
/// <summary>
/// Computes only the data log-likelihood (excludes priors).
/// </summary>
double DataLogLikelihood(double[] parameters);
/// <summary>
/// Computes only the log-prior.
/// </summary>
double PriorLogLikelihood(double[] parameters);
/// <summary>
/// Computes pointwise log-likelihood for each observation.
/// Required for WAIC and LOO-CV diagnostics.
/// </summary>
double[] PointwiseDataLogLikelihood(double[] parameters);
/// <summary>
/// Computes pointwise log-prior for each parameter.
/// </summary>
double[] PointwisePriorLogLikelihood(double[] parameters);
}The distinction between LogLikelihood and DataLogLikelihood is crucial for Bayesian inference:
-
LogLikelihoodreturns$\log p(\text{data} | \theta) + \log \pi(\theta)$ — the unnormalized log-posterior. This is what MCMC samplers maximize. -
DataLogLikelihoodreturns only$\log p(\text{data} | \theta)$ — the likelihood contribution. This is used for computing DIC, WAIC, and other model comparison criteria. -
PriorLogLikelihoodreturns$\log \pi(\theta)$ — the prior contribution. Useful for prior predictive checks.
Each parameter in a model is represented by a ModelParameter object that encapsulates:
public class ModelParameter : INotifyPropertyChanged
{
/// <summary>
/// The parameter name (for display and serialization).
/// </summary>
public string Name { get; set; }
/// <summary>
/// The current parameter value.
/// </summary>
public double Value { get; set; }
/// <summary>
/// The prior distribution for Bayesian inference.
/// </summary>
public IUnivariateDistribution PriorDistribution { get; set; }
/// <summary>
/// Lower bound for optimization and sampling.
/// </summary>
public double LowerBound { get; set; }
/// <summary>
/// Upper bound for optimization and sampling.
/// </summary>
public double UpperBound { get; set; }
/// <summary>
/// Whether the parameter is fixed (not estimated).
/// </summary>
public bool IsFixed { get; set; }
/// <summary>
/// The parameter transform for MCMC sampling.
/// </summary>
public ParameterTransform Transform { get; set; }
}Key features:
- Prior distributions: Any
IUnivariateDistributioncan serve as a prior — Normal, Uniform, Gamma, etc. - Bounds: Physical constraints (e.g., variance must be positive) are enforced via bounds
- Fixed parameters: Set
IsFixed = trueto hold a parameter constant during estimation - Transforms: Parameters can be sampled on transformed scales (log, logit) for better MCMC mixing
MCMC sampling works best when parameters are unbounded and roughly symmetric. The ParameterTransform enum provides common transformations:
| Transform | Forward | Inverse | Use Case |
|---|---|---|---|
None |
Unbounded location parameters | ||
Log |
Positive scale parameters | ||
Logit |
Parameters bounded in (0,1) | ||
Asinh |
Heavy-tailed or near-zero parameters |
When a transform is specified, MCMC sampling occurs in the transformed space
RMC-BestFit includes a comprehensive library of models for hydrologic and statistical applications.
The library supports 15 univariate probability distributions commonly used in flood frequency analysis:
| Distribution | Class | Parameters | Typical Application |
|---|---|---|---|
| Normal | Normal |
Symmetric data, transformed flows | |
| Log-Normal | LogNormal |
Right-skewed flows, rainfall | |
| Ln-Normal | LnNormal |
Alternative log-normal parameterization | |
| Exponential | Exponential |
Peaks-over-threshold excesses | |
| Gamma | GammaDistribution |
Precipitation depths | |
| Gumbel | Gumbel |
Annual maxima (light upper tail) | |
| GEV | GeneralizedExtremeValue |
Annual maxima (flexible tail) | |
| Generalized Logistic | GeneralizedLogistic |
UK flood frequency standard | |
| Generalized Normal | GeneralizedNormal |
Symmetric with flexible kurtosis | |
| Generalized Pareto | GeneralizedPareto |
Peaks-over-threshold | |
| Logistic | Logistic |
Growth curves, bounded data | |
| Kappa Four | KappaFour |
Very flexible 4-parameter family | |
| Pearson Type III | PearsonTypeIII |
General skewed data | |
| Log-Pearson Type III | LogPearsonTypeIII |
Bulletin 17C standard | |
| Weibull | Weibull |
Minima, low flows, wind speeds |
The distributions in bold are most commonly used in flood frequency analysis.
Beyond simple univariate fitting, RMC-BestFit supports:
| Model Type | Class | Description |
|---|---|---|
| Mixture | MixtureModel |
Two-population models (e.g., rain vs. snowmelt floods) |
| Competing Risks | CompetingRisksModel |
Annual maximum of multiple processes |
| Nonstationary | UnivariateDistribution with trends |
Time-varying parameters |
| Peaks-Over-Threshold | PointProcessModel |
Exceedances above threshold |
For temporal dependence modeling:
| Model | Class | Order | Description |
|---|---|---|---|
| AR(p) | AutoRegressive |
Autoregressive | |
| MA(q) | MovingAverage |
Moving average | |
| ARIMA(p,d,q) | ARIMA |
Integrated ARMA | |
| ARIMAX | ARIMAX |
|
ARIMA with exogenous variables |
Stage-discharge relationships:
| Model | Class | Segments | Parameters |
|---|---|---|---|
| Power Law | RatingCurve |
1 | |
| Two-Segment | RatingCurve |
2 | + |
| Three-Segment | RatingCurve |
3 | + |
For regional frequency analysis:
| Model | Class | Description |
|---|---|---|
| Spatial GEV | SpatialGEV |
GEV with spatially-varying parameters |
| Gaussian Copula | GaussianCopula |
Spatial dependence structure |
For joint analysis of two variables:
| Model | Class | Description |
|---|---|---|
| Bivariate | BivariateDistribution |
Copula-based joint distribution |
| Coincident Frequency | CoincidentFrequencyAnalysis |
Joint exceedance probabilities |
When the pre-built models don't fit your needs, you can create custom models by implementing the IModel interface.
Consider a simple linear regression model:
using Numerics.Distributions;
using RMC.BestFit;
using RMC.BestFit.Models;
public class LinearRegressionModel : IModel
{
private readonly double[] _x;
private readonly double[] _y;
public LinearRegressionModel(double[] x, double[] y)
{
_x = x ?? throw new ArgumentNullException(nameof(x));
_y = y ?? throw new ArgumentNullException(nameof(y));
if (x.Length != y.Length)
throw new ArgumentException("x and y must have same length");
// Initialize parameters with flat priors
Parameters = new List<ModelParameter>
{
new ModelParameter
{
Name = "α (intercept)",
Value = 0,
LowerBound = -1000,
UpperBound = 1000,
PriorDistribution = new Uniform(-1000, 1000)
},
new ModelParameter
{
Name = "β (slope)",
Value = 0,
LowerBound = -100,
UpperBound = 100,
PriorDistribution = new Uniform(-100, 100)
},
new ModelParameter
{
Name = "σ (error SD)",
Value = 1,
LowerBound = 1e-6,
UpperBound = 100,
PriorDistribution = new Uniform(1e-6, 100),
Transform = ParameterTransform.Log // Sample on log scale
}
};
}
public List<ModelParameter> Parameters { get; }
public double LogLikelihood(double[] parameters)
{
return DataLogLikelihood(parameters) + PriorLogLikelihood(parameters);
}
public double DataLogLikelihood(double[] parameters)
{
double alpha = parameters[0];
double beta = parameters[1];
double sigma = parameters[2];
if (sigma <= 0) return double.NegativeInfinity;
double logLik = 0;
for (int i = 0; i < _x.Length; i++)
{
double predicted = alpha + beta * _x[i];
double residual = _y[i] - predicted;
logLik += Normal.StandardPDF(residual / sigma) - Math.Log(sigma);
}
return logLik;
}
public double PriorLogLikelihood(double[] parameters)
{
double logPrior = 0;
for (int i = 0; i < Parameters.Count; i++)
{
if (!Parameters[i].IsFixed)
{
logPrior += Parameters[i].PriorDistribution.LogPDF(parameters[i]);
}
}
return logPrior;
}
public double[] PointwiseDataLogLikelihood(double[] parameters)
{
double alpha = parameters[0];
double beta = parameters[1];
double sigma = parameters[2];
var pointwise = new double[_x.Length];
for (int i = 0; i < _x.Length; i++)
{
double predicted = alpha + beta * _x[i];
double residual = _y[i] - predicted;
pointwise[i] = Normal.StandardPDF(residual / sigma) - Math.Log(sigma);
}
return pointwise;
}
public double[] PointwisePriorLogLikelihood(double[] parameters)
{
var pointwise = new double[Parameters.Count];
for (int i = 0; i < Parameters.Count; i++)
{
pointwise[i] = Parameters[i].IsFixed ? 0 :
Parameters[i].PriorDistribution.LogPDF(parameters[i]);
}
return pointwise;
}
}Once implemented, custom models work with all RMC-BestFit estimation methods:
// Create model with data
var model = new LinearRegressionModel(xData, yData);
// Maximum Likelihood Estimation
var mle = new MaximumLikelihood(model);
mle.Estimate();
Console.WriteLine($"MLE intercept: {mle.BestParameterSet.Values[0]:F3}");
Console.WriteLine($"MLE slope: {mle.BestParameterSet.Values[1]:F3}");
// Bayesian MCMC
var bayesian = new BayesianAnalysis(model);
bayesian.Iterations = 10000;
bayesian.WarmupIterations = 5000;
await bayesian.RunAsync();
var results = bayesian.Results;
Console.WriteLine($"Posterior mean intercept: {results.ParameterResults[0].SummaryStatistics.Mean:F3}");
Console.WriteLine($"Posterior mean slope: {results.ParameterResults[1].SummaryStatistics.Mean:F3}");-
Return
-∞for invalid parameters: If parameters violate constraints (e.g., negative variance), returndouble.NegativeInfinityrather than throwing an exception. -
Use logarithmic computation: When multiplying many probabilities, work in log-space to avoid numerical underflow.
-
Guard against numerical issues: Check for
NaN,Inf, and division by zero. -
Implement pointwise likelihoods: These are required for WAIC and LOO-CV model comparison.
-
Use appropriate transforms: Scale parameters should typically use
ParameterTransform.Log. -
Document parameter interpretations: Clear parameter names help users understand results.
The following diagram shows the inheritance relationships between model classes in RMC-BestFit:
IModel
├── ModelBase (abstract)
│ ├── UnivariateDistributionBase
│ │ ├── Normal, LogNormal, GEV, etc.
│ │ ├── MixtureModel
│ │ ├── CompetingRisksModel
│ │ └── PointProcessModel
│ ├── TimeSeriesModelBase
│ │ ├── AutoRegressive
│ │ ├── MovingAverage
│ │ ├── ARMA → ARIMA → ARIMAX
│ │ └── ARMAX
│ ├── RatingCurve
│ ├── SpatialGEV
│ └── BivariateDistribution
Primary source paths: src/RMC.BestFit/Models/Support/IModel.cs, src/RMC.BestFit/Models/Support/ModelBase.cs, src/RMC.BestFit/Models/Support/ModelParameter.cs, src/RMC.BestFit/Models/UnivariateDistribution, src/RMC.BestFit/Models/BivariateDistribution, src/RMC.BestFit/Models/RatingCurve, src/RMC.BestFit/Models/TimeSeries, and src/RMC.BestFit/Models/SpatialExtremes.
[1] Hosking, J.R.M. and Wallis, J.R. (1997). Regional Frequency Analysis: An Approach Based on L-Moments. Cambridge University Press.
[2] Coles, S. (2001). An Introduction to Statistical Modeling of Extreme Values. Springer.
[3] Gelman, A., Carlin, J.B., Stern, H.S., Dunson, D.B., Vehtari, A., and Rubin, D.B. (2013). Bayesian Data Analysis, Third Edition. CRC Press.
[4] ter Braak, C.J.F. and Vrugt, J.A. (2008). "Differential Evolution Markov Chain with snooker updater and fewer chains." Statistics and Computing, 18(4), 435-446.
<- Previous: Getting Started | Back to Index | Next: Input Data Frame ->