Document Version: 1.0
Date: October 30, 2025
Project: Casanova Drift Model (CDM)
Copyright: Bayer
This Design Specification document describes the detailed software design and implementation of the Casanova Drift Model (CDM). It provides technical information about the architecture, data structures, algorithms, and implementation details needed for development and maintenance.
This document covers:
- Software architecture and component design
- Data structures and class hierarchies
- Algorithm implementations
- Build system and dependencies
- File organization and naming conventions
- Coding standards and practices
- User Requirements Specification (URS)
- Functional Specification
- API Reference
- Build and Installation Guide
| Term | Definition |
|---|---|
| CDM | Casanova Drift Model |
| ODE | Ordinary Differential Equation |
| BDF | Backward Differentiation Formula |
| CVODE | C-language Variable-coefficient ODE solver |
| RAII | Resource Acquisition Is Initialization |
| STL | Standard Template Library |
| ABI | Application Binary Interface |
┌─────────────────────────────────────────────────────────────┐
│ Client Applications │
│ (Command-Line Tool, R Package, Custom C/C++ Applications) │
└───────────────────────────┬─────────────────────────────────┘
│
┌───────────────────────────┴─────────────────────────────────┐
│ C API Layer │
│ (CDM.h - C-compatible interface with error handling) │
└───────────────────────────┬─────────────────────────────────┘
│
┌───────────────────────────┴─────────────────────────────────┐
│ C++ Implementation Core │
├─────────────────────────────────────────────────────────────┤
│ Serialization Module │ Model Data Structure │
│ (JSON I/O) │ (Input/Output/Derived) │
├───────────────────────────┼─────────────────────────────────┤
│ Atmospheric Properties │ Wind Velocity Profile │
│ (Psychrometrics) │ (Logarithmic Law) │
├───────────────────────────┼─────────────────────────────────┤
│ Droplet Size Model │ Nozzle Velocity │
│ (Curve Fitting) │ (Initial Conditions) │
├───────────────────────────┼─────────────────────────────────┤
│ Droplet Transport │ Deposition │
│ (ODE Integration) │ (Spatial Distribution) │
└───────────────────────────┴─────────────────────────────────┘
│
┌───────────────────────────┴─────────────────────────────────┐
│ External Dependencies │
│ SUNDIALS │ Ceres │ Blaze │ Boost │ nlohmann_json │ fmt │
└─────────────────────────────────────────────────────────────┘
The following diagram (mermaid chart code) shows how inputs flow through the model processing pipeline to produce outputs, and how the different interfaces connect to the system.
flowchart TB
subgraph Inputs["📥 Inputs"]
DSD["Droplet Size<br/>Distribution<br/><i>diameter, cumulative<br/>volume fraction</i>"]
ATM["Atmospheric<br/>Properties<br/><i>temperature, pressure,<br/>relative humidity</i>"]
WIND["Wind Velocity<br/>Profile<br/><i>elevation/velocity<br/>measurements</i>"]
APP["Application<br/>Parameters<br/><i>nozzle height, pressure,<br/>angle, application rate</i>"]
FIELD["Field Geometry<br/><i>downwind depth,<br/>crosswind width,<br/>nozzle spacing</i>"]
SOL["Solution<br/>Properties<br/><i>water density,<br/>solids fraction</i>"]
end
subgraph Processing["⚙️ Model Processing"]
direction TB
JSON["JSON Parser<br/>& Validator"]
subgraph Derived["Derived Properties"]
ATMC["Atmospheric<br/>Calculations<br/><i>ρ_A, μ_A, T_wet,<br/>T_dew</i>"]
WINDC["Wind Profile<br/>Characterization<br/><i>friction velocity U_f,<br/>roughness z₀</i>"]
DSDC["DSD Processing<br/><i>curve fitting or<br/>finite difference</i>"]
NOZZLE["Nozzle Velocity<br/><i>exit velocity,<br/>streamline angles<br/>−40° to −140°</i>"]
end
subgraph Transport["ODE Integration (CVODE)"]
ODE["6-Component System<br/><i>Z, X, V_z, V_x, M_w, V_vwx</i>"]
DRAG["Drag Forces"]
EVAP["Evaporation"]
GRAV["Gravitational<br/>Settling"]
WINDINT["Wind<br/>Interaction"]
end
DEP["Deposition<br/>Calculation<br/><i>ground crossings,<br/>mass accumulation</i>"]
end
subgraph Outputs["📤 Outputs"]
DEPO["Deposition Profile<br/><i>distance vs %IAR</i>"]
VERT["Vertical Drift<br/>Profile<br/><i>concentration at<br/>downwind distances</i>"]
REPORT["Summary Report<br/><i>atmospheric props,<br/>wind profile,<br/>mass balance</i>"]
JSONOUT["JSON Results<br/><i>full input echo +<br/>all outputs</i>"]
end
subgraph Interfaces["🔗 Interfaces"]
CAPI["C API<br/><i>libcdm shared library</i>"]
CLI["CLI<br/><i>cdmcli executable</i>"]
RPKG["R Package<br/><i>cdm</i>"]
end
DSD --> JSON
ATM --> JSON
WIND --> JSON
APP --> JSON
FIELD --> JSON
SOL --> JSON
JSON --> ATMC
JSON --> WINDC
JSON --> DSDC
JSON --> NOZZLE
ATMC --> ODE
WINDC --> ODE
DSDC --> ODE
NOZZLE --> ODE
DRAG --> ODE
EVAP --> ODE
GRAV --> ODE
WINDINT --> ODE
ODE --> DEP
DEP --> DEPO
DEP --> VERT
DEP --> REPORT
DEP --> JSONOUT
CAPI --> Outputs
CLI --> Outputs
RPKG --> Outputs
style Inputs fill:#e8f4fd,stroke:#0969da,color:#24292f
style Processing fill:#f6f8fa,stroke:#57606a,color:#24292f
style Derived fill:#fff8e1,stroke:#d4a017,color:#24292f
style Transport fill:#fce4ec,stroke:#c62828,color:#24292f
style Outputs fill:#e8f5e9,stroke:#2e7d32,color:#24292f
style Interfaces fill:#f3e5f5,stroke:#7b1fa2,color:#24292f
- Provides user-facing interfaces (CLI, R bindings)
- Handles file I/O and argument parsing
- Formats output for display
- Provides stable ABI for cross-language integration
- Manages resource lifetime (creation/destruction)
- Translates between C and C++ error handling
- Implements mathematical algorithms
- Manages internal data structures
- Performs numerical computations
- ODE integration (SUNDIALS/CVODE)
- Non-linear optimization (Ceres Solver)
- Linear algebra (Blaze)
- Mathematical functions (Boost.Math)
- JSON parsing (nlohmann_json)
- String formatting (fmt)
Purpose: C API implementation and entry points
Key Components:
// Opaque model structure (pImpl idiom)
struct cdm_model_s {
cdm::Model model; // Internal C++ model
std::string output; // Cached JSON output
int status; // Execution status
};
// API functions
cdm_model_t* cdm_create_model(const char *config);
void cdm_free_model(cdm_model_t *model);
int cdm_run_model(cdm_model_t *model);
void cdm_print_report(cdm_model_t *model);
char* cdm_get_output_string(cdm_model_t *model);
void cdm_free_string(char *s);
const char* cdm_library_version(void);
cdm_error_handler_t cdm_set_error_handler(cdm_error_handler_t handler);Design Patterns:
- Opaque Pointer (pImpl): Hides C++ implementation details from C clients
- Resource Management: RAII for C++ objects, manual management for C API
- Error Handling: Global error handler with customization support
Thread Safety: Not thread-safe (single-threaded use only)
Purpose: Central data structure containing all model parameters and results
Structure:
namespace cdm {
enum class PPPMethod {
ENTERED = 0, // User-provided value
INTERPOLATE = 1, // Interpolate from table
SDTF = 2 // Spray Drift Task Force formula
};
struct Model {
// Identification
std::string name;
// Input: Droplet Size Distribution
std::vector<std::pair<double, double>> dsd; // [diameter μm, cumulative fraction]
double dpmin; // Min droplet size [μm]
double dpmax; // Max droplet size [μm]
bool dsdfit; // Enable curve fitting
// Derived: Droplet sizes and model
std::vector<double> dp; // Calculated sizes [μm]
std::unique_ptr<DropletSizeModel> dsmodel; // Fitted model
// Input: Atmospheric Properties
double Tair; // Temperature [°C]
double Patm; // Pressure [Pa]
double RH; // Relative humidity [%]
// Derived: Atmospheric Properties
double rhoA; // Wet air density [g/cm³]
double muA; // Dynamic viscosity [g·cm⁻¹·s⁻¹]
double Tdp; // Dew point [°C]
double Twb; // Wet bulb [°C]
double dTwb; // Wet bulb depression [°C]
// Input: Solution Properties
double rhoW; // Water density [g/cm³]
double rhoS; // Solids density [g/cm³]
double xs0; // Initial solids fraction
// Derived: Solution Properties
double rhoL; // Mixture density [g/cm³]
// Input: Wind Profile
double hC; // Canopy height [m]
std::vector<std::pair<double, double>> wvu; // Wind velocity measurements
std::vector<std::pair<double, double>> wvT; // Temperature measurements
std::optional<double> ppp; // Horizontal variation [deg]
PPPMethod pppMethod; // Calculation method
// Derived: Wind Profile
double pppcalc; // Calculated horizontal variation [deg]
double z0; // Friction height [m]
double Uf; // Friction velocity [m/s]
// Input: Droplet Transport
double hN; // Nozzle height [m]
double PN; // Nozzle pressure [Pa]
double thetaN; // Nozzle angle [deg]
double ddd; // Time scale factor (δδδ)
// Derived: Nozzle Velocities
std::array<double, constants::ns> nva; // Streamline angles [deg]
std::array<double, constants::ns> nvz; // Vertical velocities [m/s]
std::array<double, constants::ns> nvx; // Horizontal velocities [m/s]
// Derived: Transport Results
std::array<std::vector<double>, constants::ns> xdist; // Drift distances [m]
// Input: Deposition
double IAR; // Application rate [kg/ha]
double xactive; // Active ingredient concentration
double FD; // Field depth [m]
double PL; // Field width [m]
double dN; // Nozzle spacing [m]
std::optional<double> Lmax; // Max drift distance [m]
double lambda; // Segment scale factor
double dx; // Output interval [m]
// Results: Deposition
std::vector<std::pair<double, double>> applume; // [distance m, %IAR]
// CVODE Integration Options
double cvreltol;
std::array<double, 6> cvabstol;
int cvmaxord;
int cvmxsteps;
bool cvstldet;
int cvmaxnef;
int cvmaxcor;
int cvmaxncf;
double cvnlscoef;
// Experimental Options
std::array<bool, constants::ns> sflags; // Streamline enable flags
};
} // namespace cdmDesign Considerations:
- Data Locality: Groups related parameters together
- Optional Values: Uses
std::optional<>for truly optional parameters - Smart Pointers: Uses
std::unique_ptr<>for owned objects - Value Semantics: Most members are value types for simplicity
Purpose: JSON input/output handling
Key Functions:
namespace cdm {
// Parse JSON string into Model structure
Model parseJSON(const std::string& json);
// Serialize Model to JSON string
std::string toJSON(const Model& model);
} // namespace cdmImplementation Details:
- Uses
nlohmann::jsonlibrary for parsing/serialization - Validates all required fields during parsing
- Provides detailed error messages with field names
- Handles optional fields with sensible defaults
- Preserves precision for floating-point values
Error Handling:
- Throws
std::runtime_errorfor invalid JSON - Throws
std::domain_errorfor out-of-range values - Throws
std::logic_errorfor inconsistent parameters
Purpose: Calculate derived atmospheric properties
Interface:
namespace cdm {
class AtmosphericProperties {
public:
// Calculate all properties and update model
static void calculate(Model& model);
private:
// Individual calculations
static double dewPoint(double T, double RH);
static double wetBulb(double T, double Tdp, double P);
static double wetAirDensity(double T, double P, double RH);
static double dynamicViscosity(double T, double RH);
};
} // namespace cdmAlgorithms:
Dew Point Temperature:
Magnus-Tetens approximation:
Tdp = (b * α) / (a - α)
where α = (a*T)/(b+T) + ln(RH/100)
Constants: a = 17.27, b = 237.7°C
Wet Bulb Temperature:
Iterative solution of psychrometric equation:
e(Twb) = e(T) - P * (T - Twb) * Cp / (λ * ε)
Solved using Newton-Raphson method
Wet Air Density:
ρ = (Pd * Md + Pv * Mv) / (R * T)
where Pd = P - Pv (partial pressure dry air)
Pv = RH * Psat(T) / 100 (vapor pressure)
Dynamic Viscosity:
Sutherland's formula with moisture correction:
μ = μ0 * (T/T0)^(3/2) * (T0 + S)/(T + S)
Moisture correction factor applied
Purpose: Characterize wind profile using logarithmic law
Interface:
namespace cdm {
class WindVelocityProfile {
public:
// Fit profile to measurements and update model
static void fit(Model& model);
private:
// Calculate friction parameters
static std::pair<double, double> fitLogProfile(
const std::vector<std::pair<double, double>>& measurements,
double hC);
// Calculate horizontal variation
static double calculatePPP(const Model& model);
};
} // namespace cdmAlgorithm:
Logarithmic Profile Fitting:
u(z) = (Uf/κ) * ln((z-hC)/z0)
Linearize: u = (Uf/κ) * ln(z-hC) - (Uf/κ) * ln(z0)
Let y = u, x = ln(z-hC)
Then: y = a*x + b
where a = Uf/κ, b = -(Uf/κ)*ln(z0)
Solve: Uf = a * κ
z0 = exp(-b/a)
Uses least-squares regression on linearized form.
Purpose: Fit analytical model to droplet size distribution
Interface:
namespace cdm {
class DropletSizeModel {
public:
// Fit model to DSD data
DropletSizeModel(const std::vector<std::pair<double, double>>& dsd);
// Evaluate cumulative distribution
double cdf(double dp) const;
// Evaluate probability density
double pdf(double dp) const;
private:
std::vector<double> params_; // Model parameters
// Residual function for fitting
struct Residual {
template <typename T>
bool operator()(const T* params, T* residual) const;
};
};
} // namespace cdmModel Form:
Three-parameter log-normal CDF:
F(dp) = Φ((ln(dp) - μ) / σ)
Parameters:
- μ: location (mean of ln(dp))
- σ: scale (std dev of ln(dp))
PDF (derivative):
f(dp) = (1/(dp*σ*√(2π))) * exp(-((ln(dp)-μ)²)/(2σ²))
Fitting:
- Uses Ceres Solver for non-linear least squares
- Minimizes sum of squared residuals between model and data
- Levenberg-Marquardt algorithm for optimization
- Automatic differentiation for Jacobian
Purpose: Calculate initial droplet velocities
Interface:
namespace cdm {
class NozzleVelocity {
public:
// Calculate velocities for all streamlines
static void calculate(Model& model);
private:
// Exit velocity from Bernoulli equation
static double exitVelocity(double P, double rho);
// Velocity components for angle
static std::pair<double, double> components(double V, double theta);
};
} // namespace cdmAlgorithm:
Exit Velocity (Bernoulli Equation):
V = √(2 * ΔP / ρ)
where ΔP = gauge pressure
ρ = liquid density
Velocity Components:
For streamline angle θ (from vertical):
Vz = V * cos(θ)
Vx = V * sin(θ)
Streamline angles (NS = 11):
[-40°, -50°, -60°, -70°, -80°, -90°, -100°, -110°, -120°, -130°, -140°]
Purpose: Simulate droplet trajectories using ODE integration
Interface:
namespace cdm {
class DropletTransport {
public:
struct Params {
double z0, Uf, hN, hC, dTwb;
double rhoW, rhoS, rhoL, rhoA, muA;
double xs0, ddd;
double Ms0, Mw0;
double Vvwx0;
};
DropletTransport(const Model& m);
// Simulate single droplet trajectory
// Returns final horizontal position [m]
double operator()(double Vz0, double Vx0, double dp);
private:
Params params;
CVodeIntegrator cvi;
// ODE right-hand side function
friend int RhsFn(double t, N_Vector y, N_Vector ydot, void *user_data);
};
} // namespace cdmODE System:
State vector: [Z, X, Vz, Vx, Mw, Vvwx]
dZ/dt = Vz
dX/dt = Vx
dVz/dt = (FD_z + Vz*W + V*g*(ρA - ρP))/(Mw+Ms)
dVx/dt = (FD_x + Vx*W)/(Mw+Ms)
dMw/dt = -W
dVvwx/dt = Vz * Uf/(κ*(Z-hC)) if Z > z0, else 0
where:
FD_z = drag force (vertical)
FD_x = drag force (horizontal)
W = evaporation rate
V = droplet volume
ρP = particle density = (Mw+Ms)/V
Implementation Details:
- Uses CVODE from SUNDIALS library
- BDF method for stiff ODEs
- Adaptive time stepping
- Configurable error tolerances
- Stops integration when droplet reaches ground
Purpose: Calculate spatial deposition distribution
Interface:
namespace cdm {
// Calculate deposition profile
std::vector<std::pair<double, double>> Deposition(
double IAR, // Application rate [kg/ha]
double xactive, // Concentration
double FD, // Field depth [m]
double PL, // Field width [m]
double dN, // Nozzle spacing [m]
double ppp, // Horizontal variation [deg]
double rhoL, // Liquid density [g/cm³]
const std::vector<double>& dp, // Droplet sizes
const std::array<std::vector<double>, constants::ns>& xdist, // Drift distances
const std::vector<std::pair<double, double>>& dsd, // DSD data
const std::unique_ptr<DropletSizeModel>& dsdmodel, // DSD model
double dpmin, // Min droplet size [μm]
double dpmax, // Max droplet size [μm]
std::optional<double> Lmax, // Max drift distance [m]
double lambda, // Segment scale factor
double dx, // Output interval [m]
const std::array<bool, constants::ns>& sflags); // Streamline flags
} // namespace cdmAlgorithm:
- Generate drift distance matrix: Interpolate transport results to uniform droplet sizes
- Discretize domain: Create spray and drift segments
- Calculate spray volumes: Distribute total spray volume by droplet size using DSD
- Build deposition matrices: Track which droplets reach which segments
- Apply plume spreading: Account for horizontal wind variation
- Calculate concentrations: Convert volumes to deposition rates
- Generate output profile: Interpolate to desired output spacing
Data Structures:
// Blaze matrix library for efficient linear algebra
blaze::DynamicMatrix<double> driftdist; // [streamline, droplet size]
blaze::DynamicMatrix<double> DVM; // Deposition volume matrix
blaze::DynamicMatrix<double> CM; // Concentration matrix
blaze::DynamicVector<double> SVP; // Spray volume by droplet sizePurpose: C++ wrapper for SUNDIALS CVODE solver
Interface:
namespace cdm {
class CVodeIntegrator {
public:
using RhsFunc = int (*)(double t, N_Vector y, N_Vector ydot, void* user_data);
CVodeIntegrator();
~CVodeIntegrator();
// Initialize solver
void init(RhsFunc f, double t0, std::initializer_list<double> y0);
// Reinitialize with new initial conditions
void reinit(double t0, std::initializer_list<double> y0);
// Set parameters
void setUserData(void* user_data);
void setTolerances(double reltol, const std::array<double, 6>& abstol);
void setMaxOrd(int maxord);
void setMaxNumSteps(int mxsteps);
void setStabLimDet(bool stldet);
void setMaxErrTestFails(int maxnef);
void setMaxNonlinIters(int maxcor);
void setMaxConvFails(int maxncf);
void setNonlinConvCoef(double nlscoef);
// Integration
void step(double tout);
// Access solution
std::vector<double> solution() const;
// Statistics
int getNumSteps() const;
int getNumRhsEvals() const;
// ... other statistics
private:
void* cvode_mem_;
N_Vector y_;
SUNMatrix A_;
SUNLinearSolver LS_;
};
} // namespace cdmDesign Considerations:
- RAII wrapper manages CVODE resources
- Throws
cvode::system_erroron integration failure - Provides convenient C++ interface to C library
- Exception-safe (cleans up on errors)
Purpose: Define physical and numerical constants
namespace cdm {
namespace constants {
constexpr double karman = 0.41; // von Kármán constant
constexpr double liquid_sheet_offset = 0.068; // Nozzle sheet offset [m]
constexpr double zeta = 1.0; // Plume spreading factor
constexpr int ns = 11; // Number of streamlines
constexpr int nout = 1000; // ODE output points
} // namespace constants
} // namespace cdmBuild Tool: CMake 3.15+
Package Manager: vcpkg
Supported Platforms:
- Windows (MSVC, MinGW)
- Linux (GCC, Clang)
- macOS (Clang, Apple Silicon)
CDM/
├── CMakeLists.txt # Main build configuration
├── CMakePresets.json # Build presets
├── vcpkg.json # Dependency manifest
├── vcpkg-configuration.json # vcpkg configuration
├── README.md # User documentation
├── .clang-format # Code formatting rules
├── .gitignore # Git ignore patterns
│
├── cmake/ # CMake modules
│ ├── JoinPaths.cmake
│ ├── cdm-config.cmake.in
│ └── cdm.pc.in
│
├── include/ # Public headers
│ └── cdm/
│ └── CDM.h # C API header
│
├── src/ # Implementation sources
│ ├── CDM.cpp # C API implementation
│ ├── Model.hpp # Model data structure
│ ├── Serialization.cpp/hpp # JSON I/O
│ ├── AtmosphericProperties.cpp/hpp
│ ├── WindVelocityProfile.cpp/hpp
│ ├── DropletSizeModel.cpp/hpp
│ ├── NozzleVelocity.cpp/hpp
│ ├── DropletTransport.cpp/hpp
│ ├── Deposition.cpp/hpp
│ ├── CVodeIntegrator.hpp # CVODE wrapper
│ ├── CVodeError.hpp # CVODE error handling
│ ├── Interpolate1D.hpp # Interpolation utilities
│ ├── Constants.hpp # Physical constants
│ ├── CLI11.hpp # Command-line parser
│ └── CDMCLI.cpp # CLI application
│
├── tests/ # Test cases
│ ├── Case_B.json
│ ├── Case_G.json
│ └── Case_I.json
│
├── docs/ # Documentation
│ ├── URS.md
│ ├── FunctionalSpecification.md
│ └── DesignSpecification.md
│
├── R/ # R package
│ └── cdm/
│ ├── DESCRIPTION
│ ├── NAMESPACE
│ ├── R/
│ ├── src/
│ ├── inst/
│ └── demo/
│
└── ports/ # vcpkg custom ports
├── blaze/
└── sundials/
CMakeLists.txt Structure:
cmake_minimum_required(VERSION 3.15)
# Version extraction from header
file(READ include/cdm/CDM.h CDM_H)
# Parse CDM_VERSION
project(cdm VERSION ${CDM_VERSION} LANGUAGES CXX)
# Options
option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
# Dependencies
find_package(Boost REQUIRED)
find_package(blaze CONFIG REQUIRED)
find_package(Ceres CONFIG REQUIRED)
find_package(fmt CONFIG REQUIRED)
find_package(SUNDIALS CONFIG REQUIRED)
find_package(nlohmann_json CONFIG REQUIRED)
# Library target
add_library(cdm ${SOURCES})
target_link_libraries(cdm PRIVATE ${DEPENDENCIES})
target_include_directories(cdm PUBLIC ...)
# CLI target
add_executable(cdmcli src/CDMCLI.cpp)
target_link_libraries(cdmcli PRIVATE cdm::cdm fmt::fmt)
# Install rules
install(TARGETS cdm cdmcli ...)vcpkg.json Dependencies:
{
"name": "cdm",
"version": "1.2.0",
"dependencies": [
"boost-math",
"blaze",
"ceres",
"fmt",
"sundials",
"nlohmann-json"
]
}C++ Standard: C++17
Required Features:
- Structured bindings
- std::optional
- std::unique_ptr
- Lambda expressions
- Range-based for loops
- Variadic templates
Compiler Versions:
- GCC 7.0+
- Clang 5.0+
- MSVC 19.14+ (Visual Studio 2017 15.7+)
- AppleClang 10.0+
Principles:
- RAII: All resources managed by scope-based lifetime
- Smart Pointers: Use
std::unique_ptrfor owned objects - Value Semantics: Prefer value types over pointers when possible
- No Raw new/delete: Use standard containers and smart pointers
Example:
struct Model {
// Value types
double Tair;
std::vector<double> dp;
// Smart pointer for polymorphic object
std::unique_ptr<DropletSizeModel> dsmodel;
// Optional for truly optional data
std::optional<double> Lmax;
};| Use Case | Container | Rationale |
|---|---|---|
| Droplet sizes | std::vector<double> |
Sequential access, dynamic size |
| DSD data | std::vector<std::pair<double, double>> |
Ordered pairs |
| Drift distances | std::array<std::vector<double>, NS> |
Fixed outer size, variable inner |
| Deposition output | std::vector<std::pair<double, double>> |
Sequential results |
| Streamline flags | std::array<bool, NS> |
Fixed size, efficient |
| Matrices | blaze::DynamicMatrix<double> |
Linear algebra operations |
Floating Point: double (64-bit IEEE 754)
Rationale:
- Required precision for scientific computation
- Standard in numerical libraries (CVODE, Ceres, Blaze)
- Adequate range for physical quantities
Integer Types:
int: General purpose integersize_t: Array indices and sizesptrdiff_t: Pointer differences
Internal: std::string for all string operations
API Boundary:
- Input:
const char*(C string) - Output:
char*(allocated by library, freed by caller)
Rationale:
std::stringprovides safety and convenience internally- C strings provide ABI stability for API
Solver Selection:
- CVODE from SUNDIALS library
- Method: BDF (Backward Differentiation Formula) for stiff equations
- Order: Adaptive, max order 5 (default)
Stiffness Handling:
- Droplet dynamics can be stiff due to:
- Rapid evaporation for small droplets
- Large drag forces for low velocities
- Multiple time scales
- BDF method handles stiffness efficiently
Error Control:
- Relative tolerance: 1×10⁻⁴
- Absolute tolerances: Component-specific
- Position: 1×10⁻⁸ cm
- Velocity: 1×10⁻⁸ cm/s
- Mass: 1×10⁻¹⁰ g
1D Interpolation:
- Method: Linear interpolation with optional extrapolation
- Implementation: Custom
Interpolate1Dtemplate class - Features:
- Clamps extrapolated values to bounds (optional)
- Throws exception for invalid domains
- Efficient binary search for lookup
Log-space Interpolation:
- Used for drift distances at small angles (streamlines 0-5)
- Improves accuracy for exponential-like behavior
- Formula:
exp(interpolate(log(x), log(y)))
Non-linear Least Squares:
- Library: Ceres Solver
- Method: Levenberg-Marquardt
- Automatic Differentiation: Jet types for Jacobian
- Termination: Function tolerance or parameter tolerance
DSD Curve Fitting:
struct Residual {
Residual(double dp_obs, double f_obs)
: dp_obs_(dp_obs), f_obs_(f_obs) {}
template <typename T>
bool operator()(const T* const params, T* residual) const {
T dp = T(dp_obs_);
T f_model = CDF(dp, params); // Model CDF
residual[0] = f_model - T(f_obs_);
return true;
}
double dp_obs_;
double f_obs_;
};Library: Blaze
Operations:
- Matrix-vector multiplication
- Element-wise operations
- Reductions (sum, max)
- Submatrix views
Performance:
- Expression templates for optimization
- SIMD vectorization
- Cache-friendly access patterns
C++ Layer:
- Exceptions for error conditions
- std::runtime_error for runtime failures
- std::domain_error for invalid arguments
- std::logic_error for programming errors
- cvode::system_error for CVODE failures
C API Layer:
- Return codes (0 = success, non-zero = error)
- Error handler callback for messages
- No exceptions across C boundary
Global Error Handler:
static cdm_error_handler_t error_handler = default_error_handler;
cdm_error_handler_t cdm_set_error_handler(cdm_error_handler_t handler) {
cdm_error_handler_t old = error_handler;
error_handler = handler ? handler : default_error_handler;
return old;
}
void cdm_error(const char *format, ...) {
va_list args;
va_start(args, format);
error_handler(format, args);
va_end(args);
}Default Handler:
void default_error_handler(const char *format, ...) {
va_list args;
va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
}Guarantees:
- Basic: No resource leaks, objects in valid state
- Strong: Operation succeeds or has no effect (where feasible)
- No-throw: Destructors and move operations
Techniques:
- RAII for all resources
- Smart pointers prevent leaks
- Standard containers provide strong guarantee
- Careful exception boundaries at API
Test Levels:
- Unit Tests: Individual component testing
- Integration Tests: Component interaction testing
- System Tests: End-to-end testing with SETAC cases
- Validation Tests: Comparison with reference data
SETAC DRAW Test Cases:
- Case B: AXI 11002, 250 kPa, no surfactant
- Case G: XR 11004, 300 kPa, Agral surfactant
- Case I: XR 11004, 250 kPa, no surfactant
Validation Criteria:
- Model runs without errors
- Results are physically reasonable
- Mass balance is conserved
- Output format is correct
Command-Line Testing:
# Run test case
./cdmcli tests/Case_B.json
# Verify exit code
echo $? # Should be 0
# Check output exists
ls -l output.jsonR Package Testing:
# Run demo
demo("caseB", package="cdm")
# Verify results
result <- cdm_run("tests/Case_B.json")
stopifnot(result$statistics$totalMass > 0.99)
stopifnot(result$statistics$totalMass < 1.01)- Correctness: Accuracy over speed
- Maintainability: Clarity over micro-optimization
- Scalability: Efficient algorithms over constant factors
Algorithmic:
- Efficient ODE solver (CVODE)
- Adaptive time stepping
- Sparse matrix operations where applicable
- Vectorized operations (Blaze)
Data Structure:
- Cache-friendly memory layout
- Minimize allocations in hot loops
- Use stack allocation where possible
- Preallocate containers
Compiler:
- Enable optimization flags (-O3, /O2)
- Link-time optimization (LTO)
- Profile-guided optimization (PGO) for production
Expected Performance:
- Case B: ~15 seconds
- Case G: ~20 seconds
- Case I: ~25 seconds
Bottlenecks:
- ODE integration: 80-90% of runtime
- Deposition calculation: 5-10% of runtime
- I/O and initialization: <5% of runtime
Formatter: clang-format
Configuration:
BasedOnStyle: LLVM
IndentWidth: 4
ColumnLimit: 100
PointerAlignment: LeftNaming Conventions:
- Types: PascalCase (
Model,DropletTransport) - Functions: camelCase (
calculateDewPoint,parseJSON) - Variables: camelCase (
dewPoint,frictionVelocity) - Constants: lowercase with underscores (
liquid_sheet_offset) - Macros: UPPER_CASE (
CDM_VERSION)
Header Comments:
// Copyright (c) 2021 John Buonagurio <jbuonagurio@exponent.com>Function Comments:
/**
* Calculate dew point temperature from dry bulb temperature and RH.
* \param[in] T Dry bulb temperature [°C]
* \param[in] RH Relative humidity [%]
* \return Dew point temperature [°C]
*/
double dewPoint(double T, double RH);Inline Comments:
- Explain "why" not "what"
- Document assumptions and limitations
- Reference equations or papers where appropriate
- Follows coding style
- Includes appropriate comments
- No memory leaks (checked with sanitizers)
- Exception-safe (RAII, no raw pointers)
- Const-correct
- Input validation
- Error handling
- Unit tests (if applicable)
- Documentation updated
Formats:
- Source: GitHub repository
- Binary: vcpkg package
- R Package: Source tarball
Installation Methods:
- Build from source using CMake
- Install via vcpkg
- Install R package from source
Scheme: Semantic Versioning (MAJOR.MINOR.PATCH)
Version Definition:
#define CDM_VERSION 10200 // 1.2.0
#define CDM_VERSION_STRING "1.2.0"Version Access:
const char* version = cdm_library_version(); // Returns "1.2.0"ABI Compatibility:
- C API provides stable ABI
- C++ internals can change between versions
- Semantic versioning guides compatibility
Platform Compatibility:
- Windows x64 (static and shared libraries)
- Linux x64 (shared libraries)
- macOS x64 and ARM64 (shared libraries)
JSON Parsing:
- Use trusted JSON library (nlohmann_json)
- Validate schema before processing
- Reject malformed JSON
- Limit nesting depth
- Limit array/string sizes
Parameter Validation:
- Range checks on all numerical inputs
- Consistency checks on related parameters
- Reject NaN and infinity values
- Prevent integer overflow
Techniques:
- RAII for all resources
- Smart pointers prevent leaks
- Bounds checking on array access
- Standard containers prevent buffer overflows
- No manual memory management in user code
Tools:
- AddressSanitizer for memory errors
- LeakSanitizer for memory leaks
- UndefinedBehaviorSanitizer for UB
- Static analysis (clang-tidy)
Resource Limits:
- Maximum ODE steps (cvmxsteps)
- Maximum integration time
- Reasonable parameter ranges
- Early termination conditions
Update Frequency:
- Security patches: Immediate
- Bug fixes: As needed
- Feature updates: Quarterly review
Update Process:
- Review changelog
- Update vcpkg manifest
- Test build and run
- Update documentation if needed
- Commit changes
Regular Tasks:
- Update dependencies
- Review and merge pull requests
- Address bug reports
- Update documentation
- Performance profiling
Tools:
- GitHub for version control
- GitHub Actions for CI/CD
- GitHub Issues for bug tracking
- GitHub Projects for planning
| Version | Date | Author | Description |
|---|---|---|---|
| 1.0 | 2025-10-30 | CDM Team | Initial Design Specification |
End of Design Specification