diff --git a/.agents/skills/ptf-extract/SKILL.md b/.agents/skills/ptf-extract/SKILL.md index a3be592..d300f40 100644 --- a/.agents/skills/ptf-extract/SKILL.md +++ b/.agents/skills/ptf-extract/SKILL.md @@ -17,17 +17,41 @@ input error. ## Procedure -1. Read the supplied local paper, `specs/schema/ptf-spec.schema.json`, and +1. Read the supplied local paper, `specs/schema/ptf-spec.schema.json`, + `specs/quantities.yaml`, `specs/units.yaml`, and `references/extraction-quality-gate.md`. If the source defines a finite categorical input or a table selected by that input, also read `references/categorical-lookups.md`. -2. Extract only facts explicitly supported by the paper. Write its standalone +2. Resolve every scalar output and record field to a stable quantity identifier + and normalized unit identifier allowed by `specs/quantities.yaml`. Preserve the + literal source notation in `reported_unit` and check it against the preferred + notation or aliases in `specs/units.yaml`. If an identifier, allowed pair, or + equivalent notation is absent, stop and ask + the user to review a proposed identifier or unit entry, absolute tolerance, + optional relative tolerance, and concise rationale. Include the output + description and nearest analogous entries. Add the policy only after the + user accepts or edits it; never silently invent a registry entry. +3. Extract only facts explicitly supported by the paper. Write its standalone YAML directly to `specs/functions/.yaml`, following `references/spec-template.yaml`. -3. Record missing or ambiguous required scientific information as explicit +4. Add every source-published input-output example as a `published` + `verification_cases` entry. If none exists, select physically meaningful + inputs and calculate the expected outputs with simple reference code as + described in `references/extraction-quality-gate.md`. Give each case an ID + that is unique within its function and valid as a Rust test function name. + Use descriptive lowercase `snake_case` identifiers and avoid Rust keywords. + Specification validation checks uniqueness but does not validate Rust + identifier syntax. Invalid names can cause Rust code generation or subsequent + compilation to fail even after specification validation succeeds. +5. If the publication explicitly states an applicable output accuracy or + resolution, optionally propose a function-level `verification_tolerances` + override keyed by output name and cite its exact `source_location`. Do not + search indefinitely for one, put an override in a verification case, or use + model-performance statistics such as RMSE as implementation tolerances. +6. Record missing or ambiguous computational-contract information as explicit blockers and set affected functions to `blocked`; otherwise set reviewed, complete functions to `ready-for-implementation`. -4. Run `cargo run --manifest-path codegen/Cargo.toml -- validate` and fix +7. Run `cargo run --manifest-path codegen/Cargo.toml -- validate` and fix validation errors before finishing. When a nontrivial formula expression is repeated within one implementation, declare it once as an earlier local implementation variable and reference that variable; retain the published @@ -42,14 +66,23 @@ exact YAML path and explicit blockers. ## Hard rules - Do not set `implemented`, run target generation, or edit generated files. -- Do not invent formulas, units, metadata, golden values, applicability, or - API details. Keep uncertainty explicit in the YAML. +- Do not invent formulas, units, published examples, applicability, or API + details. Calculated verification inputs must satisfy the documented domain + and physical constraints; fix independently calculated expected values in + YAML and explain the choice in `rationale`. +- Do not call a calculated case independent, source-native, artifact-native, + external validation, or evidence of model accuracy. - Do not normalize, alias, abbreviate, or otherwise broaden source-defined categorical values. Keep enum member names, canonical textual values, lookup rows, and their evidence distinct. - Give every `type: record` output a PascalCase `name`, whether it is inline or declared in `$defs`. It names generated structures and classes; `$defs` keys only resolve local `$ref` targets. +- Give every scalar output and record field a reviewed `quantity`, normalized + `unit`, and literal `reported_unit`. Resolve aliases only within the quantity + context and only when they preserve numerical values exactly. Never convert + values through alias resolution. Tolerances belong to the quantity-unit pair; + the unit registry owns identity and notation only. - Use `generation.public_python: manual` only when the public wrapper cannot follow the standard generated API; it never opts the native NumPy ufunc out of generation. diff --git a/.agents/skills/ptf-extract/references/categorical-lookups.md b/.agents/skills/ptf-extract/references/categorical-lookups.md index c501f2a..a2da2bc 100644 --- a/.agents/skills/ptf-extract/references/categorical-lookups.md +++ b/.agents/skills/ptf-extract/references/categorical-lookups.md @@ -43,7 +43,7 @@ field names. Invoke the lookup as an ordered implementation variable. Its `key` names an in-scope input of the lookup's enum type. Return a compatible record-valued variable directly, or use `variable.field` in later formula expressions. -Golden-test categorical inputs also use enum member schema names. +Verification-case categorical inputs also use enum member schema names. ```yaml $defs: @@ -73,12 +73,12 @@ functions: table: $ref: "#/$defs/ParametersByTexture" key: texture - golden_tests: + verification_cases: - id: coarse_table_row + kind: published inputs: {texture: coarse} expected: {coefficient: 1.25} - rtol: 0.0 - atol: 0.0 + source_location: "Published parameter table, coarse row" notes: Direct published table row. ``` diff --git a/.agents/skills/ptf-extract/references/extraction-quality-gate.md b/.agents/skills/ptf-extract/references/extraction-quality-gate.md index 4d4bc5a..8625ae1 100644 --- a/.agents/skills/ptf-extract/references/extraction-quality-gate.md +++ b/.agents/skills/ptf-extract/references/extraction-quality-gate.md @@ -8,9 +8,14 @@ APA-style slug and identifies the generated public module, for example ## Required facts - Preserve the ordered functions, inputs, outputs, source metadata, units, - golden and edge cases, documentation, scope, and semantic `implementation` + verification and edge cases, documentation, scope, and semantic `implementation` fields required by the schema. - Every record output has an explicit PascalCase `name`. +- Every scalar output and record field has a stable `quantity` and normalized + `unit` allowed by `specs/quantities.yaml`. Its literal `reported_unit` matches + the unit preferred notation or an alias in `specs/units.yaml` without changing + the numerical value. Registry tolerances are reviewed project + policy, not claims of measurement uncertainty or model predictive accuracy. - When the source uses a finite categorical predictor, represent its reusable type as an enum in `$defs` and bind it to each function-local argument with `name` plus `$ref`. The enum owns its type description and admissible values; @@ -18,7 +23,7 @@ APA-style slug and identifies the generated public module, for example - When the source publishes a table selected by a category, model it as a typed lookup from the enum to a record in `$defs`. Preserve one row per enum member and one numeric value per output-record field. Use enum member names in lookup - keys and golden inputs, not canonical textual values or target ordinals. + keys and verification-case inputs, not canonical textual values or target ordinals. - Use the formula DSL only in `implementation` expressions. In `scientific_notes`, retain source-supported scientific context, derivations needed to justify an interpretation, evidence for review decisions, citations, @@ -35,13 +40,44 @@ APA-style slug and identifies the generated public module, for example `generation.public_python: manual` only for an intentional manual public wrapper; native ufunc generation remains required. +## Verification cases + +Every computationally complete function needs at least one verification case. +Use only these provenance kinds: + +- `published` when a complete input-output pair appears in the paper, + supplementary material, official author software, or another authoritative + source. Preserve its location in `source_location`. +- `calculated` when no complete published pair exists. Select an input and + independently calculate the expected output from the published computational + contract, fix the result in YAML, and explain the input choice in `rationale`. + +Prefer calculated inputs in this order: a complete published predictor row, +published predictor means or medians, an interior point of the documented +domain, then an expert-selected typical physically valid soil. Keep values away +from boundaries, use the stated units and scales, preserve texture sums and +positive logarithm arguments, and respect physical relations such as +`theta_1500 <= theta_33 <= theta_s`. Do not combine unrelated marginal extrema. +For piecewise models and trees, cover every material computational branch with +a published case where available and a calculated case otherwise. + +For record outputs, `expected` contains every output field. Verification cases +contain provenance but no tolerance or precision fields. Generated tests use +the resolved function-output policy from `specs/quantities.yaml` or an explicit +function-level `verification_tolerances` override with `source_location`. + +The lack of a published case is not an extraction or release blocker. + ## Blockers Set affected functions to `blocked` and name the missing evidence when a -formula, constant, unit, output mapping, semantic expression, golden value, -numeric policy, or applicability fact is missing or ambiguous. Do not use +formula, constant, unit, output mapping, preprocessing step, transform, branch, +fitted payload, or applicability fact is missing or ambiguous. Do not use `TODO` as a substitute for a structured required value; write it only in an -explicit blocker note. Schema-valid YAML may still be blocked. +explicit blocker note. The absence of a published example, row-level dataset, +supplement, or author calculator is not a blocker when the computational +contract is complete enough to create a calculated case. Rights and licensing +remain a separate release gate and do not change scientific extraction status. ## Statuses diff --git a/.agents/skills/ptf-extract/references/spec-template.yaml b/.agents/skills/ptf-extract/references/spec-template.yaml index 1fe3a39..7b5c1a7 100644 --- a/.agents/skills/ptf-extract/references/spec-template.yaml +++ b/.agents/skills/ptf-extract/references/spec-template.yaml @@ -18,8 +18,21 @@ functions: h_theta: null k_h: null inputs: [] - outputs: [] - golden_tests: [] + outputs: + type: scalar + name: target + quantity: reviewed_quantity_identifier + symbol: null + unit: reviewed_unit_identifier + reported_unit: source unit + domain: null + description: Source-supported output description. + verification_cases: + - id: representative_case + kind: calculated + inputs: {} + expected: {} + rationale: Explain why the input is physically meaningful and within the source domain. edge_cases: [] documentation: notes: [] diff --git a/.agents/skills/ptf-review/SKILL.md b/.agents/skills/ptf-review/SKILL.md index 7296227..6fabcb3 100644 --- a/.agents/skills/ptf-review/SKILL.md +++ b/.agents/skills/ptf-review/SKILL.md @@ -19,7 +19,7 @@ modifying repository state. 1. Read `specs/schema/ptf-spec.schema.json`, the selected YAML specification, implementation diff, generated Rust, C, C++, and native NumPy targets, - golden tests, and relevant public wrapper. + verification tests, and relevant public wrapper. 2. Load `references/implementation-review-checklist.md`. 3. Check schema and semantic IR fidelity, all retained targets, deterministic regeneration, status transition evidence, categorical type and lookup @@ -40,7 +40,7 @@ modifying repository state. - Treat the YAML specification and semantic implementation as the sole numerical source of truth, not guessed article intent. -- Treat missing golden tests, ambiguous units, and public API breaks as +- Treat missing verification tests, ambiguous units, and public API breaks as blocking unless explicitly approved. - Verify that each record `outputs.name` is PascalCase and names generated structures and classes. diff --git a/.agents/skills/ptf-review/references/implementation-review-checklist.md b/.agents/skills/ptf-review/references/implementation-review-checklist.md index 757d825..336f492 100644 --- a/.agents/skills/ptf-review/references/implementation-review-checklist.md +++ b/.agents/skills/ptf-review/references/implementation-review-checklist.md @@ -10,7 +10,7 @@ - [ ] Every categorical argument binds its function-local name to the intended enum type; type and optional binding descriptions retain their separate roles. - [ ] Enum member names, exact canonical textual values, order, and optional - descriptions match the source-supported specification. Golden inputs use + descriptions match the source-supported specification. Verification inputs use member names, never textual values or generated ordinals. - [ ] Every lookup maps the declared enum to the declared record, covers each member exactly once, and gives each row exactly the record fields. Lookup @@ -29,7 +29,7 @@ lookup conversion, record shape, and numeric computation from the semantic IR. - [ ] Generated native NumPy ufuncs use the same IR and private ordinal encoding only as a target implementation detail. -- [ ] Generated target tests cover every structured golden case. +- [ ] Generated target tests cover every structured verification case. - [ ] Valid IR unsupported by a retained target is reported as a generator capability blocker, not replaced with hand-written computation. @@ -57,5 +57,5 @@ Classify as blocking: schema or semantic failure; formula, unit, output-order, categorical-type, lookup, or public-API mismatch; missing retained target or -golden test; unsupported IR; nondeterministic generation; unsubstantiated +verification test; unsupported IR; nondeterministic generation; unsubstantiated status transition; or exposed repository-only specification paths. diff --git a/codegen/README.md b/codegen/README.md index 408738b..97b83fe 100644 --- a/codegen/README.md +++ b/codegen/README.md @@ -7,8 +7,10 @@ contract; internal generator APIs are not. ## Pipeline -1. `specs` loads source specifications and `validate` checks their contracts. -2. `compile` resolves formulas and golden cases into `CompiledFunction` values. +1. `specs` loads source specifications and `specs/quantities.yaml` and `specs/units.yaml`; `validate` + checks output coverage and their remaining contracts. +2. `compile` resolves formulas and fixed verification cases into + `CompiledFunction` values and validates their complete input-output shape. 3. `documentation` provides borrowed source/function facts without target markup. `render` contains shared text, Markdown, and C-family expression rendering support. @@ -50,12 +52,15 @@ mise run corpus-report --format json The command uses the normal specification loader, schema and semantic validation, and compilation path. It counts every schema-valid function, including `draft` and `blocked` functions. Verification coverage counts declared -`golden_tests` and `edge_cases`; it does not describe external predictive +`verification_cases` and `edge_cases`; it does not describe external predictive validation on soil datasets, and a declared edge case is not necessarily an -executable test. +executable test. Provenance counts distinguish published examples from +calculated implementation checks. The JSON document has stable top-level `sources`, `functions`, `verification`, -`inputs`, `outputs`, `scope`, and `blocked_functions` sections. Category tables +`quantity_registry`, `inputs`, `outputs`, `scope`, and `blocked_functions` +sections. Quantity-registry coverage reports used and unused quantity-unit +entries and whether outputs use registry defaults or cited source overrides. Category tables are emitted as sorted arrays with explicit counts and percentages. Inputs are resolved by the specification loader and reported separately as `numeric` or `categorical`. diff --git a/codegen/src/compile.rs b/codegen/src/compile.rs index dd3b1a7..75ba5f4 100644 --- a/codegen/src/compile.rs +++ b/codegen/src/compile.rs @@ -1,10 +1,12 @@ use anyhow::{Context, Result}; use crate::model::{ - CompiledFunction, CompiledGoldenTest, CompiledInput, CoreFunction, Entry, Function, - GoldenInput, Output, Outputs, + CompiledFunction, CompiledInput, CompiledTolerance, CompiledVerificationCase, CoreFunction, + Entry, Function, Output, Outputs, ToleranceSource, VerificationInput, }; +pub(crate) const FLOATING_POINT_GUARD: f64 = 1e-14; + pub(super) fn functions(entries: Vec) -> Result> { let mut compiled = Vec::new(); for entry in entries { @@ -33,8 +35,12 @@ pub(super) fn functions(entries: Vec) -> Result> { .collect(), output, }; + let verification_cases = verification_cases(function)?; + let output_tolerances = output_tolerances(&entry, function)?; + validate_discriminating_tolerances(function, &verification_cases, &output_tolerances)?; compiled.push(CompiledFunction { - golden_tests: golden_tests(function)?, + verification_cases, + output_tolerances, core, entry: entry.clone(), function_index, @@ -45,11 +51,19 @@ pub(super) fn functions(entries: Vec) -> Result> { Ok(compiled) } -fn golden_tests(function: &Function) -> Result> { +fn verification_cases(function: &Function) -> Result> { function - .golden_tests + .verification_cases .iter() .map(|case| { + for name in case.inputs.keys() { + if !function.inputs.iter().any(|input| input.name() == name) { + anyhow::bail!( + "verification case `{}` references unknown input `{name}`", + case.id + ); + } + } let inputs = function .inputs .iter() @@ -57,20 +71,20 @@ fn golden_tests(function: &Function) -> Result> { let input_name = input.name(); let value = case.inputs.get(input_name).with_context(|| { format!( - "golden test `{}` is missing input `{}`", + "verification case `{}` is missing input `{}`", case.id, input_name ) })?; match (input.enum_type(), value) { - (None, GoldenInput::Number(value)) => Ok(CompiledInput::Number(*value)), - (Some(enum_type), GoldenInput::Enum(member_name)) => { + (None, VerificationInput::Number(value)) => Ok(CompiledInput::Number(*value)), + (Some(enum_type), VerificationInput::Enum(member_name)) => { enum_type .values .iter() .find(|member| member.name == *member_name) .with_context(|| { format!( - "golden test `{}` input `{}` references unknown member `{member_name}` of enum `{}`", + "verification case `{}` input `{}` references unknown member `{member_name}` of enum `{}`", case.id, input_name, enum_type.name ) })?; @@ -79,13 +93,13 @@ fn golden_tests(function: &Function) -> Result> { member_name: member_name.clone(), }) } - (None, GoldenInput::Enum(_)) => anyhow::bail!( - "golden test `{}` input `{}` must be numeric", + (None, VerificationInput::Enum(_)) => anyhow::bail!( + "verification case `{}` input `{}` must be numeric", case.id, input_name ), - (Some(enum_type), GoldenInput::Number(_)) => anyhow::bail!( - "golden test `{}` input `{}` must name a member of enum `{}`", + (Some(enum_type), VerificationInput::Number(_)) => anyhow::bail!( + "verification case `{}` input `{}` must name a member of enum `{}`", case.id, input_name, enum_type.name @@ -93,6 +107,14 @@ fn golden_tests(function: &Function) -> Result> { } }) .collect::>>()?; + for name in case.expected.keys() { + if !function.outputs.fields().iter().any(|field| field.name == *name) { + anyhow::bail!( + "verification case `{}` references unknown output `{name}`", + case.id + ); + } + } let expected = function .outputs .fields() @@ -100,18 +122,90 @@ fn golden_tests(function: &Function) -> Result> { .map(|field| { case.expected.get(&field.name).copied().with_context(|| { format!( - "golden test `{}` is missing output `{}`", + "verification case `{}` is missing expected output `{}`", case.id, field.name ) }) }) .collect::>>()?; - Ok(CompiledGoldenTest { + Ok(CompiledVerificationCase { id: case.id.clone(), inputs, expected, - rtol: case.rtol, - atol: case.atol, + }) + }) + .collect() +} + +fn validate_discriminating_tolerances( + function: &Function, + cases: &[CompiledVerificationCase], + tolerances: &[CompiledTolerance], +) -> Result<()> { + for case in cases { + for ((field, expected), tolerance) in function + .outputs + .fields() + .iter() + .zip(&case.expected) + .zip(tolerances) + { + let magnitude = expected.abs(); + let resolved = tolerance.absolute.max(tolerance.relative * magnitude); + if *expected != 0.0 && resolved >= magnitude { + anyhow::bail!( + "function `{}` verification case `{}` output `{}` has resolved scientific tolerance {} (absolute {}, relative {}) greater than or equal to the magnitude of non-zero expected value {}", + function.name, + case.id, + field.name, + resolved, + tolerance.absolute, + tolerance.relative, + magnitude, + ); + } + } + } + Ok(()) +} + +fn output_tolerances(entry: &Entry, function: &Function) -> Result> { + function + .outputs + .fields() + .iter() + .map(|field| { + if let Some(override_) = function.verification_tolerances.get(&field.name) { + return Ok(CompiledTolerance { + absolute: override_.absolute, + relative: override_.relative.unwrap_or_default(), + quantity: field.quantity.clone(), + unit: field.unit.clone(), + source: ToleranceSource::SourceOverride(override_.source_location.clone()), + }); + } + let quantity = entry + .quantities + .quantities + .get(&field.quantity) + .with_context(|| { + format!( + "function `{}` output `{}` references unknown quantity `{}`", + function.name, field.name, field.quantity + ) + })?; + let tolerance = quantity.units.get(&field.unit).with_context(|| { + format!( + "function `{}` output `{}` quantity `{}` has no registered unit `{}`", + function.name, field.name, field.quantity, field.unit + ) + })?; + Ok(CompiledTolerance { + absolute: tolerance.absolute, + relative: tolerance.relative.unwrap_or_default(), + quantity: field.quantity.clone(), + unit: field.unit.clone(), + source: ToleranceSource::Registry, }) }) .collect() diff --git a/codegen/src/corpus_report.rs b/codegen/src/corpus_report.rs index 6f5bce4..ccdac50 100644 --- a/codegen/src/corpus_report.rs +++ b/codegen/src/corpus_report.rs @@ -4,13 +4,14 @@ use std::collections::{BTreeMap, BTreeSet}; use serde::Serialize; -use crate::model::{Entry, Input, Outputs}; +use crate::model::{Entry, Input, Outputs, VerificationKind}; #[derive(Debug, Serialize)] pub(crate) struct Report { pub(crate) sources: Sources, pub(crate) functions: Functions, pub(crate) verification: Verification, + pub(crate) quantity_registry: QuantityRegistryReport, pub(crate) inputs: Vec, pub(crate) outputs: OutputsReport, pub(crate) scope: ScopeReport, @@ -36,17 +37,23 @@ pub(crate) struct Functions { #[derive(Debug, Serialize)] pub(crate) struct Verification { + pub(crate) cases_total: usize, + pub(crate) by_kind: BTreeMap<&'static str, usize>, + pub(crate) published_cases: usize, + pub(crate) calculated_cases: usize, + pub(crate) functions_without_cases: usize, pub(crate) all_functions: VerificationCoverage, pub(crate) implemented_functions: VerificationCoverage, + pub(crate) ready_for_implementation_functions: VerificationCoverage, pub(crate) edge_case_interpretation: &'static str, } #[derive(Debug, Default, Serialize)] pub(crate) struct VerificationCoverage { pub(crate) functions: usize, - pub(crate) golden_tests: usize, - pub(crate) functions_with_golden_tests: usize, - pub(crate) functions_with_golden_tests_percentage: f64, + pub(crate) verification_cases: usize, + pub(crate) functions_with_verification_cases: usize, + pub(crate) functions_with_verification_cases_percentage: f64, pub(crate) edge_cases: usize, pub(crate) functions_with_edge_cases: usize, pub(crate) functions_with_edge_cases_percentage: f64, @@ -75,6 +82,21 @@ pub(crate) struct OutputsReport { pub(crate) structured_property_grouping_available: bool, } +#[derive(Debug, Serialize)] +pub(crate) struct QuantityRegistryReport { + pub(crate) registered_quantities: usize, + pub(crate) quantity_unit_combinations_in_use: usize, + pub(crate) outputs_using_registry_defaults: usize, + pub(crate) outputs_using_source_specific_overrides: usize, + pub(crate) unused_quantity_unit_entries: Vec, +} + +#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Serialize)] +pub(crate) struct QuantityUnitEntry { + pub(crate) quantity: String, + pub(crate) unit: String, +} + #[derive(Debug, Serialize)] pub(crate) struct ScopeReport { pub(crate) prediction_targets: Vec, @@ -147,10 +169,20 @@ impl Report { let mut record_functions = 0; let mut all_verification = VerificationCoverage::default(); let mut implemented_verification = VerificationCoverage::default(); + let mut ready_verification = VerificationCoverage::default(); + let mut verification_by_kind = VerificationKind::ALL + .into_iter() + .map(|kind| (kind.label(), 0)) + .collect::>(); + let mut published_cases = 0; + let mut calculated_cases = 0; let mut function_territories = BTreeMap::new(); let mut source_territories = BTreeMap::new(); let mut functions_with_territory = 0; let mut blocked_functions = Vec::new(); + let mut used_quantity_units = BTreeSet::new(); + let mut registry_defaults = 0; + let mut source_overrides = 0; for entry in entries { if let Some(territory) = &entry.spec.scope.territory { @@ -191,12 +223,30 @@ impl Report { } for field in function.outputs.fields() { increment(&mut output_fields, &field.name); + used_quantity_units.insert((field.quantity.clone(), field.unit.clone())); + if function.verification_tolerances.contains_key(&field.name) { + source_overrides += 1; + } else { + registry_defaults += 1; + } } add_verification(&mut all_verification, function); + for case in &function.verification_cases { + *verification_by_kind + .get_mut(case.kind.label()) + .expect("all verification kinds are initialized") += 1; + match case.kind { + VerificationKind::Published => published_cases += 1, + VerificationKind::Calculated => calculated_cases += 1, + } + } if function.status == "implemented" { add_verification(&mut implemented_verification, function); } + if function.status == "ready-for-implementation" { + add_verification(&mut ready_verification, function); + } if function.status == "blocked" { blocked_functions.push(BlockedFunction { source_identifier: entry.slug.clone(), @@ -212,10 +262,29 @@ impl Report { } finish_verification(&mut all_verification); finish_verification(&mut implemented_verification); + finish_verification(&mut ready_verification); blocked_functions.sort_by(|left, right| { (&left.source_identifier, &left.function_name) .cmp(&(&right.source_identifier, &right.function_name)) }); + let registry = entries.first().map(|entry| entry.quantities.as_ref()); + let registered_quantity_units = registry + .into_iter() + .flat_map(|registry| ®istry.quantities) + .flat_map(|(id, quantity)| { + quantity + .units + .keys() + .map(move |unit| (id.clone(), unit.clone())) + }) + .collect::>(); + let unused_quantity_unit_entries = registered_quantity_units + .difference(&used_quantity_units) + .map(|(quantity, unit)| QuantityUnitEntry { + quantity: quantity.clone(), + unit: unit.clone(), + }) + .collect(); let territory_names = source_territories .keys() @@ -252,10 +321,24 @@ impl Report { by_status: status_frequencies(statuses, total_functions), }, verification: Verification { + cases_total: all_verification.verification_cases, + by_kind: verification_by_kind, + published_cases, + calculated_cases, + functions_without_cases: all_verification.functions + - all_verification.functions_with_verification_cases, all_functions: all_verification, implemented_functions: implemented_verification, + ready_for_implementation_functions: ready_verification, edge_case_interpretation: "declared specification metadata; not a claim that the cases are executable or externally validated", }, + quantity_registry: QuantityRegistryReport { + registered_quantities: registry.map_or(0, |registry| registry.quantities.len()), + quantity_unit_combinations_in_use: used_quantity_units.len(), + outputs_using_registry_defaults: registry_defaults, + outputs_using_source_specific_overrides: source_overrides, + unused_quantity_unit_entries, + }, inputs: sorted_inputs(inputs, total_functions), outputs: OutputsReport { scalar_functions, @@ -320,6 +403,17 @@ impl Report { } output.push_str("\nVerification\n------------\n"); + output.push_str(&format!( + "Total verification cases: {}\nPublished cases: {}\nCalculated cases: {}\nFunctions without verification cases: {}\n", + self.verification.cases_total, + self.verification.published_cases, + self.verification.calculated_cases, + self.verification.functions_without_cases, + )); + output.push_str("Cases by provenance:\n"); + for (kind, count) in &self.verification.by_kind { + output.push_str(&format!(" {kind}: {count}\n")); + } render_verification( &mut output, "All functions", @@ -330,6 +424,24 @@ impl Report { "Implemented functions", &self.verification.implemented_functions, ); + render_verification( + &mut output, + "Ready-for-implementation functions", + &self.verification.ready_for_implementation_functions, + ); + + output.push_str("\nQuantity registry\n-----------------\n"); + output.push_str(&format!( + "Registered quantities: {}\nQuantity-unit combinations in use: {}\nOutputs using registry defaults: {}\nOutputs using source-specific overrides: {}\n", + self.quantity_registry.registered_quantities, + self.quantity_registry.quantity_unit_combinations_in_use, + self.quantity_registry.outputs_using_registry_defaults, + self.quantity_registry.outputs_using_source_specific_overrides, + )); + output.push_str("Unused quantity-unit entries:\n"); + for entry in &self.quantity_registry.unused_quantity_unit_entries { + output.push_str(&format!(" {} [{}]\n", entry.quantity, entry.unit)); + } output.push_str("\nInputs\n------\n"); for input in &self.inputs { @@ -415,15 +527,18 @@ fn publication_year(slug: &str) -> Option { fn add_verification(coverage: &mut VerificationCoverage, function: &crate::model::Function) { coverage.functions += 1; - coverage.golden_tests += function.golden_tests.len(); + coverage.verification_cases += function.verification_cases.len(); coverage.edge_cases += function.edge_cases.len(); - coverage.functions_with_golden_tests += usize::from(!function.golden_tests.is_empty()); + coverage.functions_with_verification_cases += + usize::from(!function.verification_cases.is_empty()); coverage.functions_with_edge_cases += usize::from(!function.edge_cases.is_empty()); } fn finish_verification(coverage: &mut VerificationCoverage) { - coverage.functions_with_golden_tests_percentage = - percentage(coverage.functions_with_golden_tests, coverage.functions); + coverage.functions_with_verification_cases_percentage = percentage( + coverage.functions_with_verification_cases, + coverage.functions, + ); coverage.functions_with_edge_cases_percentage = percentage(coverage.functions_with_edge_cases, coverage.functions); } @@ -508,10 +623,10 @@ fn status_label(status: &str) -> String { fn render_verification(output: &mut String, label: &str, coverage: &VerificationCoverage) { output.push_str(&format!( - "{label}:\n Golden tests: {}\n Functions with golden tests: {} ({:.1}%)\n Edge cases: {}\n Functions with edge cases: {} ({:.1}%)\n", - coverage.golden_tests, - coverage.functions_with_golden_tests, - coverage.functions_with_golden_tests_percentage, + "{label}:\n Verification cases: {}\n Functions with verification cases: {} ({:.1}%)\n Edge cases: {}\n Functions with edge cases: {} ({:.1}%)\n", + coverage.verification_cases, + coverage.functions_with_verification_cases, + coverage.functions_with_verification_cases_percentage, coverage.edge_cases, coverage.functions_with_edge_cases, coverage.functions_with_edge_cases_percentage @@ -530,7 +645,10 @@ fn render_values(output: &mut String, values: &[Frequency]) { #[cfg(test)] mod tests { - use std::path::{Path, PathBuf}; + use std::{ + path::{Path, PathBuf}, + sync::Arc, + }; use super::{InputKind, Report}; use crate::model::{Entry, Spec}; @@ -543,6 +661,7 @@ mod tests { slug: slug.to_owned(), spec, implementations, + quantities: Arc::default(), } } @@ -585,9 +704,9 @@ functions: - {$ref: '#/$defs/x'} - {$ref: '#/$defs/x'} outputs: {$ref: '#/$defs/Result'} - golden_tests: - - {id: one, inputs: {x: 1.0}, expected: {alpha: 1.0, zeta: 2.0}, rtol: 0.0, atol: 0.0} - - {id: two, inputs: {x: 2.0}, expected: {alpha: 2.0, zeta: 3.0}, rtol: 0.0, atol: 0.0} + verification_cases: + - {id: one, kind: published, inputs: {x: 1.0}, expected: {alpha: 1.0, zeta: 2.0}, source_location: 'Table 1, row 1'} + - {id: two, kind: calculated, inputs: {x: 2.0}, expected: {alpha: 2.0, zeta: 3.0}, rationale: Interior input.} edge_cases: - {id: edge, inputs: {x: 0.0}, expected_behavior: Finite., notes: Metadata only.} - name: calc_ptf_test_blocked @@ -599,7 +718,7 @@ functions: inputs: - {$ref: '#/$defs/Category', name: category} outputs: {type: scalar, name: beta, symbol: b, unit: '1', domain: null, description: Beta.} - golden_tests: [] + verification_cases: [] edge_cases: [] documentation: notes: [Known source limitation.] @@ -636,13 +755,17 @@ functions: .iter() .all(|status| status.count == 1) ); - assert_eq!(report.verification.all_functions.golden_tests, 2); + assert_eq!(report.verification.all_functions.verification_cases, 2); + assert_eq!(report.verification.by_kind["published"], 1); + assert_eq!(report.verification.by_kind["calculated"], 1); + assert_eq!(report.verification.published_cases, 1); + assert_eq!(report.verification.calculated_cases, 1); assert_eq!(report.verification.all_functions.edge_cases, 1); assert_eq!( report .verification .all_functions - .functions_with_golden_tests, + .functions_with_verification_cases, 1 ); assert_eq!(report.verification.implemented_functions.functions, 1); @@ -710,10 +833,13 @@ functions: let report = Report::from_entries(&[fixture()]); let first = serde_json::to_string_pretty(&report).expect("report serializes"); let second = serde_json::to_string_pretty(&report).expect("report serializes again"); + let text = report.to_text(); assert_eq!(first, second); assert!(first.contains("\"blocked_functions\"")); assert!(first.contains("\"implemented_functions\"")); + assert!(!first.contains("missing_quantity_or_unit_validation_failures")); + assert!(text.find("Implemented functions") < text.find("Quantity registry")); } #[test] @@ -726,6 +852,9 @@ functions: crate::compile::functions(entries.clone()).expect("repository specifications compile"); let report = Report::from_entries(&entries); + assert_eq!(report.verification.cases_total, 136); + assert_eq!(report.verification.by_kind["calculated"], 125); + assert_eq!(report.verification.by_kind["published"], 11); assert_eq!(report.sources.specification_files, entries.len()); assert_eq!( report.functions.total, diff --git a/codegen/src/documentation.rs b/codegen/src/documentation.rs index 3cdbf75..ed64f66 100644 --- a/codegen/src/documentation.rs +++ b/codegen/src/documentation.rs @@ -3,7 +3,7 @@ //! Targets choose their own section ordering and markup. This module only //! describes the information they have available to render. -use crate::model::{Function, Input, Outputs, Parameter, Scope, Source}; +use crate::model::{Function, Input, OutputField, Outputs, Parameter, Scope, Source}; #[derive(Clone, Copy, Debug)] pub(crate) struct SourceDocument<'a> { @@ -39,10 +39,10 @@ pub(crate) struct FunctionDocument<'a> { #[derive(Clone, Copy, Debug)] pub(crate) enum Returns<'a> { - Scalar(&'a Parameter), + Scalar(&'a OutputField), Record { name: &'a str, - fields: &'a [Parameter], + fields: &'a [OutputField], }, } @@ -111,6 +111,18 @@ impl ParameterMetadata for Parameter { } } +impl ParameterMetadata for OutputField { + fn name(&self) -> &str { + &self.name + } + fn description(&self) -> &str { + &self.description + } + fn unit(&self) -> Option<&str> { + Some(&self.reported_unit) + } +} + impl ParameterMetadata for Input { fn name(&self) -> &str { self.name() @@ -137,7 +149,8 @@ pub(crate) fn parameter_documentation(parameter: &impl ParameterMetadata) -> Str #[cfg(test)] mod tests { use crate::model::{ - Documentation, Function, FunctionScope, Input, Models, Outputs, Parameter, PublicApi, + Documentation, Function, FunctionScope, Input, Models, OutputField, Outputs, Parameter, + PublicApi, }; use super::{ParameterMetadata, Returns, for_function, parameter_details}; @@ -158,10 +171,13 @@ mod tests { } } - fn parameter(name: &str) -> Parameter { - Parameter { + fn output(name: &str) -> OutputField { + OutputField { + reported_unit: "cm^3/cm^3".into(), name: name.into(), - unit: "cm^3/cm^3".into(), + quantity: "volumetric_water_content".into(), + symbol: None, + unit: "volume_fraction".into(), domain: None, description: format!("{name} description."), } @@ -201,11 +217,12 @@ mod tests { }, inputs: Vec::new(), outputs: Outputs::Scalar { - field: parameter("result"), + field: output("result"), }, + verification_tolerances: Default::default(), documentation: Documentation::default(), implementation: None, - golden_tests: Vec::new(), + verification_cases: Vec::new(), edge_cases: Vec::new(), }; @@ -239,14 +256,15 @@ mod tests { inputs: vec![input("sand")], outputs: Outputs::Record { name: "TestResult".into(), - fields: vec![parameter("theta_33"), parameter("theta_1500")], + fields: vec![output("theta_33"), output("theta_1500")], }, + verification_tolerances: Default::default(), documentation: Documentation { notes: vec!["A note.".into()], warnings: vec!["A warning.".into()], }, implementation: None, - golden_tests: Vec::new(), + verification_cases: Vec::new(), edge_cases: Vec::new(), }; diff --git a/codegen/src/main.rs b/codegen/src/main.rs index 86022b0..7c7991b 100644 --- a/codegen/src/main.rs +++ b/codegen/src/main.rs @@ -56,6 +56,7 @@ impl Cli { match self.command { Command::Validate => { let entries = load_validated_specifications(root)?; + compile::functions(entries.clone())?; println!( "validated {} PTF specification files containing {} functions", entries.len(), diff --git a/codegen/src/model.rs b/codegen/src/model.rs index 54f0c4c..b2c037e 100644 --- a/codegen/src/model.rs +++ b/codegen/src/model.rs @@ -1,6 +1,9 @@ -use std::{collections::BTreeMap, path::PathBuf}; +use std::{collections::BTreeMap, path::PathBuf, sync::Arc}; -use serde::{Deserialize, Deserializer, Serialize}; +use serde::{ + Deserialize, Deserializer, Serialize, + de::{Error as _, MapAccess, Visitor}, +}; #[derive(Clone, Debug, Serialize)] pub(crate) struct Spec { @@ -45,9 +48,11 @@ struct FunctionReference { scope: FunctionScope, inputs: Vec, outputs: OutputReference, + #[serde(default)] + verification_tolerances: BTreeMap, implementation: Option, #[serde(default)] - golden_tests: Vec, + verification_cases: Vec, #[serde(default)] edge_cases: Vec, #[serde(default)] @@ -186,8 +191,9 @@ impl<'de> Deserialize<'de> for Spec { scope: function.scope, inputs, outputs, + verification_tolerances: function.verification_tolerances, implementation, - golden_tests: function.golden_tests, + verification_cases: function.verification_cases, edge_cases: function.edge_cases, documentation: function.documentation, }) @@ -373,9 +379,11 @@ pub(crate) struct Function { pub(crate) scope: FunctionScope, pub(crate) inputs: Vec, pub(crate) outputs: Outputs, + #[serde(default)] + pub(crate) verification_tolerances: BTreeMap, pub(crate) implementation: Option, #[serde(default)] - pub(crate) golden_tests: Vec, + pub(crate) verification_cases: Vec, #[serde(default)] pub(crate) edge_cases: Vec, #[serde(default)] @@ -392,17 +400,37 @@ impl Function { } #[derive(Clone, Debug, Deserialize, Serialize)] -pub(crate) struct GoldenTest { +pub(crate) struct VerificationCase { pub(crate) id: String, - pub(crate) inputs: BTreeMap, + pub(crate) kind: VerificationKind, + pub(crate) inputs: BTreeMap, pub(crate) expected: BTreeMap, - pub(crate) rtol: f64, - pub(crate) atol: f64, + pub(crate) source_location: Option, + pub(crate) rationale: Option, + pub(crate) notes: Option, +} + +#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)] +#[serde(rename_all = "lowercase")] +pub(crate) enum VerificationKind { + Published, + Calculated, +} + +impl VerificationKind { + pub(crate) const ALL: [Self; 2] = [Self::Published, Self::Calculated]; + + pub(crate) const fn label(self) -> &'static str { + match self { + Self::Published => "published", + Self::Calculated => "calculated", + } + } } #[derive(Clone, Debug, Deserialize, Serialize)] #[serde(untagged)] -pub(crate) enum GoldenInput { +pub(crate) enum VerificationInput { Number(f64), Enum(String), } @@ -440,6 +468,22 @@ pub(crate) struct Parameter { pub(crate) description: String, } +#[derive(Clone, Debug, Deserialize, Serialize)] +pub(crate) struct OutputField { + pub(crate) name: String, + #[serde(default)] + pub(crate) quantity: String, + pub(crate) symbol: Option, + /// Stable identifier in the unit registry. + pub(crate) unit: String, + /// Literal source notation; normalization never changes numerical values. + #[serde(default)] + pub(crate) reported_unit: String, + #[allow(dead_code)] + pub(crate) domain: Option, + pub(crate) description: String, +} + #[derive(Clone, Debug, Serialize)] #[serde(untagged)] pub(crate) enum Input { @@ -544,16 +588,16 @@ pub(crate) struct LookupValue { pub(crate) enum Outputs { Scalar { #[serde(flatten)] - field: Parameter, + field: OutputField, }, Record { name: String, - fields: Vec, + fields: Vec, }, } impl Outputs { - pub(crate) fn fields(&self) -> &[Parameter] { + pub(crate) fn fields(&self) -> &[OutputField] { match self { Self::Scalar { field } => std::slice::from_ref(field), Self::Record { fields, .. } => fields, @@ -609,12 +653,84 @@ pub(crate) struct LookupInvocation { pub(crate) definition: Option, } +#[derive(Clone, Debug, Deserialize, Serialize)] +pub(crate) struct VerificationToleranceOverride { + pub(crate) absolute: f64, + #[serde(default)] + pub(crate) relative: Option, + pub(crate) source_location: String, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(deny_unknown_fields)] +pub(crate) struct QuantityUnit { + pub(crate) absolute: f64, + #[serde(default)] + pub(crate) relative: Option, + pub(crate) rationale: String, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(deny_unknown_fields)] +pub(crate) struct Quantity { + pub(crate) description: String, + #[serde(deserialize_with = "deserialize_unique_units")] + pub(crate) units: BTreeMap, +} + +fn deserialize_unique_units<'de, D>( + deserializer: D, +) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + struct UniqueUnitsVisitor; + + impl<'de> Visitor<'de> for UniqueUnitsVisitor { + type Value = BTreeMap; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("a map with unique unit keys") + } + + fn visit_map(self, mut map: A) -> Result + where + A: MapAccess<'de>, + { + let mut units = BTreeMap::new(); + while let Some((unit, tolerance)) = map.next_entry()? { + if units.insert(unit, tolerance).is_some() { + return Err(A::Error::custom("duplicate entry in quantity units")); + } + } + Ok(units) + } + } + + deserializer.deserialize_map(UniqueUnitsVisitor) +} + +#[derive(Clone, Debug, Deserialize)] +#[serde(deny_unknown_fields)] +pub(crate) struct Unit { + pub(crate) preferred_notation: String, + #[serde(default)] + pub(crate) aliases: Vec, +} + +#[derive(Clone, Debug, Default)] +pub(crate) struct QuantityRegistry { + pub(crate) quantities: BTreeMap, + pub(crate) units: BTreeMap, +} + #[derive(Clone, Debug)] pub(crate) struct Entry { pub(crate) path: PathBuf, pub(crate) slug: String, pub(crate) spec: Spec, pub(crate) implementations: Vec>, + pub(crate) quantities: Arc, } /// A validated source function paired with its immutable semantic IR. @@ -624,16 +740,30 @@ pub(crate) struct CompiledFunction { pub(crate) function_index: usize, pub(crate) ir: crate::semantic::Function, pub(crate) core: CoreFunction, - pub(crate) golden_tests: Vec, + pub(crate) verification_cases: Vec, + pub(crate) output_tolerances: Vec, } #[derive(Clone, Debug)] -pub(crate) struct CompiledGoldenTest { +pub(crate) struct CompiledVerificationCase { pub(crate) id: String, pub(crate) inputs: Vec, pub(crate) expected: Vec, - pub(crate) rtol: f64, - pub(crate) atol: f64, +} + +#[derive(Clone, Debug)] +pub(crate) struct CompiledTolerance { + pub(crate) absolute: f64, + pub(crate) relative: f64, + pub(crate) quantity: String, + pub(crate) unit: String, + pub(crate) source: ToleranceSource, +} + +#[derive(Clone, Debug)] +pub(crate) enum ToleranceSource { + Registry, + SourceOverride(String), } #[derive(Clone, Debug)] @@ -688,7 +818,7 @@ pub(crate) struct RawVariable { #[derive(Clone, Debug)] pub(crate) enum RawVariableValue { Expression(RawExpression), - Lookup(RawLookup), + Lookup(Box), } #[derive(Clone, Debug)] diff --git a/codegen/src/output/mod.rs b/codegen/src/output/mod.rs index 9bd9735..52b4575 100644 --- a/codegen/src/output/mod.rs +++ b/codegen/src/output/mod.rs @@ -108,6 +108,13 @@ layout!( C_HEADER, Formatter::Cpp ); +layout!( + NATIVE_TEST_SUPPORT, + "targets/ptfkit-native/tests/support", + "targets/ptfkit-native/tests/support", + C_HEADER, + Formatter::Cpp +); layout!( NATIVE_C_TEST, "targets/ptfkit-native/tests/c", @@ -123,7 +130,7 @@ layout!( Formatter::Cpp ); -pub(crate) const LAYOUTS: [&Layout; 12] = [ +pub(crate) const LAYOUTS: [&Layout; 13] = [ &CATALOG, &REFERENCE_C, &REFERENCE_CPP, @@ -134,6 +141,7 @@ pub(crate) const LAYOUTS: [&Layout; 12] = [ &PYTHON_TEST, &NATIVE_C, &NATIVE_CPP_MODULE, + &NATIVE_TEST_SUPPORT, &NATIVE_C_TEST, &NATIVE_CPP_TEST, ]; diff --git a/codegen/src/semantic.rs b/codegen/src/semantic.rs index 7093f2f..2b52185 100644 --- a/codegen/src/semantic.rs +++ b/codegen/src/semantic.rs @@ -736,7 +736,7 @@ mod tests { use crate::{ formula::parse, model::{ - EnumDefinition, LookupDefinition, Outputs, Parameter, RawExpression, RawFunction, + EnumDefinition, LookupDefinition, OutputField, Outputs, RawExpression, RawFunction, RawInput, RawInputType, RawLookup, RawVariable, RawVariableValue, SourceLocation, }, }; @@ -842,9 +842,12 @@ mod tests { lookup_definition.input_type = Some(enum_type.clone()); lookup_definition.output_type = Some(Outputs::Record { name: "ExampleRecord".into(), - fields: vec![Parameter { + fields: vec![OutputField { name: "value".into(), - unit: "1".into(), + quantity: String::new(), + symbol: None, + unit: "dimensionless".into(), + reported_unit: "1".into(), domain: None, description: "Example value.".into(), }], @@ -859,11 +862,11 @@ mod tests { }], variables: vec![RawVariable { name: "row".into(), - value: RawVariableValue::Lookup(RawLookup { + value: RawVariableValue::Lookup(Box::new(RawLookup { implementation_path: "implementation.variables[0].lookup".into(), key: "kind".into(), definition: lookup_definition, - }), + })), }], }; let compiled = compile(&raw).unwrap(); diff --git a/codegen/src/specs.rs b/codegen/src/specs.rs index 065b8ae..33e1d15 100644 --- a/codegen/src/specs.rs +++ b/codegen/src/specs.rs @@ -1,19 +1,21 @@ -use std::{fs, path::Path}; +use std::{fs, path::Path, sync::Arc}; -use anyhow::{Result, bail}; +use anyhow::{Context, Result, bail}; use jsonschema::Draft; use serde_json::Value; use crate::{ formula, model::{ - Entry, Implementation, ImplementationVariable, Input, RawExpression, RawFunction, RawInput, - RawInputType, RawLookup, RawVariable, RawVariableValue, Spec, + Entry, Implementation, ImplementationVariable, Input, Quantity, QuantityRegistry, + RawExpression, RawFunction, RawInput, RawInputType, RawLookup, RawVariable, + RawVariableValue, Spec, }, semantic, }; pub(crate) fn load(root: &Path) -> Result> { + let quantities = load_quantities(root)?; let schema: Value = serde_json::from_slice(&fs::read(root.join("specs/schema/ptf-spec.schema.json"))?)?; let validator = jsonschema::options() @@ -113,6 +115,7 @@ pub(crate) fn load(root: &Path) -> Result> { slug, spec, implementations, + quantities: Arc::clone(&quantities), }), Err(error) => errors.push(error), } @@ -123,6 +126,25 @@ pub(crate) fn load(root: &Path) -> Result> { Ok(entries) } +fn load_quantities(root: &Path) -> Result> { + let path = root.join("specs/quantities.yaml"); + let text = fs::read_to_string(&path) + .with_context(|| format!("reading quantity registry {}", path.display()))?; + // Parse through Value to reject duplicate keys at every map level. + let value = serde_yaml::from_str::(&text) + .with_context(|| format!("reading quantity registry {}", path.display()))?; + let quantities: std::collections::BTreeMap = serde_yaml::from_value(value) + .with_context(|| format!("reading quantity registry {}", path.display()))?; + let path = root.join("specs/units.yaml"); + let text = fs::read_to_string(&path) + .with_context(|| format!("reading unit registry {}", path.display()))?; + let value = serde_yaml::from_str::(&text) + .with_context(|| format!("reading unit registry {}", path.display()))?; + let units = serde_yaml::from_value(value) + .with_context(|| format!("reading unit registry {}", path.display()))?; + Ok(Arc::new(QuantityRegistry { quantities, units })) +} + fn source_slug(path: &Path) -> Result { let Some(stem) = path.file_stem().and_then(|stem| stem.to_str()) else { return Err(format!( @@ -199,13 +221,13 @@ fn compile( .expect("lookup invocation is resolved"); Ok(RawVariable { name: name.clone(), - value: RawVariableValue::Lookup(RawLookup { + value: RawVariableValue::Lookup(Box::new(RawLookup { implementation_path: format!( "implementation.variables[{index}].lookup" ), key: lookup.key.clone(), definition, - }), + })), }) } }) @@ -392,12 +414,29 @@ mod tests { root.join("specs/schema/ptf-spec.schema.json"), ) .unwrap(); + fs::copy( + Path::new(env!("CARGO_MANIFEST_DIR")).join("../specs/quantities.yaml"), + root.join("specs/quantities.yaml"), + ) + .unwrap(); + fs::copy( + Path::new(env!("CARGO_MANIFEST_DIR")).join("../specs/units.yaml"), + root.join("specs/units.yaml"), + ) + .unwrap(); root } fn specification(slug: &str, implementation: &str, generation: &str) -> String { + let verification = if implementation.is_empty() + || implementation.contains("verification_cases:") + { + "" + } else { + " verification_cases:\n - {id: reference, kind: calculated, inputs: {x: 1.0}, expected: {value: 1.0}, rationale: Test fixture.}\n" + }; format!( - "source:\n summary: Test source.\n citation_apa: Test (2026).\n doi: null\n{generation}functions:\n - name: calc_ptf_{slug}\n status: ready-for-implementation\n public_api: {{name: calc_ptf_{slug}, result_class: null, summary: Test value.}}\n scope:\n prediction_target: Test value.\n models: {{h_theta: null, k_h: null}}\n inputs:\n - {{name: x, symbol: x, unit: '1', domain: null, description: Test input.}}\n outputs: {{type: scalar, name: value, symbol: y, unit: '1', domain: null, description: Test output.}}\n{implementation}" + "source:\n summary: Test source.\n citation_apa: Test (2026).\n doi: null\n{generation}functions:\n - name: calc_ptf_{slug}\n status: ready-for-implementation\n public_api: {{name: calc_ptf_{slug}, result_class: null, summary: Test value.}}\n scope:\n prediction_target: Test value.\n models: {{h_theta: null, k_h: null}}\n inputs:\n - {{name: x, symbol: x, unit: '1', domain: null, description: Test input.}}\n outputs: {{type: scalar, name: value, quantity: volumetric_water_content, symbol: y, unit: volume_fraction, reported_unit: '1', domain: null, description: Test output.}}\n{implementation}{verification}" ) } @@ -444,7 +483,7 @@ $defs: type: record name: Parameters fields: - - {name: factor, symbol: f, unit: '1', domain: null, description: Test factor.} + - {name: factor, quantity: volumetric_water_content, symbol: f, unit: volume_fraction, reported_unit: '1', domain: null, description: Test factor.} ParametersByTexture: type: lookup input: {$ref: '#/$defs/Texture'} @@ -462,7 +501,7 @@ functions: inputs: - {$ref: '#/$defs/Texture', name: texture} - {name: x, symbol: x, unit: '1', domain: null, description: Test input.} - outputs: {type: scalar, name: value, symbol: y, unit: '1', domain: null, description: Test output.} + outputs: {type: scalar, name: value, quantity: volumetric_water_content, symbol: y, unit: volume_fraction, reported_unit: '1', domain: null, description: Test output.} implementation: variables: - name: parameters @@ -470,6 +509,8 @@ functions: table: {$ref: '#/$defs/ParametersByTexture'} key: texture - {name: value, expr: parameters.factor * x} + verification_cases: + - {id: sand, kind: calculated, inputs: {texture: sand, x: 1.0}, expected: {value: 2.0}, rationale: Test fixture.} "#; fs::write( root.join("specs/functions/record_lookup_expression.yaml"), @@ -538,7 +579,7 @@ functions: ) .replace( "functions:\n", - "$defs:\n Texture:\n type: enum\n description: Texture class.\n values:\n - {name: sand, value: sand}\n - {name: clay, value: clay}\n Parameters:\n type: record\n name: Parameters\n fields:\n - {name: factor, symbol: f, unit: '1', domain: null, description: Test factor.}\n InvalidLookup:\n type: lookup\n input: {$ref: '#/$defs/Texture'}\n output: {$ref: '#/$defs/Parameters'}\n values:\n - {key: sand, value: {factor: 2.0}}\nfunctions:\n", + "$defs:\n Texture:\n type: enum\n description: Texture class.\n values:\n - {name: sand, value: sand}\n - {name: clay, value: clay}\n Parameters:\n type: record\n name: Parameters\n fields:\n - {name: factor, quantity: volumetric_water_content, symbol: f, unit: volume_fraction, reported_unit: '1', domain: null, description: Test factor.}\n InvalidLookup:\n type: lookup\n input: {$ref: '#/$defs/Texture'}\n output: {$ref: '#/$defs/Parameters'}\n values:\n - {key: sand, value: {factor: 2.0}}\nfunctions:\n", ); fs::write( root.join("specs/functions/unused_invalid_lookup.yaml"), @@ -568,7 +609,7 @@ $defs: type: record name: Parameters fields: - - {name: factor, symbol: f, unit: '1', domain: null, description: Test factor.} + - {name: factor, quantity: volumetric_water_content, symbol: f, unit: volume_fraction, reported_unit: '1', domain: null, description: Test factor.} ParametersByTexture: type: lookup input: {$ref: '#/$defs/Texture'} @@ -588,7 +629,7 @@ functions: type: record name: Parameters fields: - - {name: other, symbol: o, unit: '1', domain: null, description: Other value.} + - {name: other, quantity: volumetric_water_content, symbol: o, unit: volume_fraction, reported_unit: '1', domain: null, description: Other value.} implementation: variables: - name: parameters @@ -621,7 +662,8 @@ functions: .replace( "{name: x, symbol: x, unit: '1', domain: null, description: Test input.}", "{name: value, symbol: x, unit: '1', domain: null, description: Test input.}", - ); + ) + .replace("inputs: {x: 1.0}", "inputs: {value: 1.0}"); fs::write( root.join("specs/functions/input_output.yaml"), specification, @@ -642,8 +684,8 @@ functions: "", ) .replace( - "outputs: {type: scalar, name: value, symbol: y, unit: '1', domain: null, description: Test output.}", - "outputs:\n type: record\n fields:\n - {name: first, symbol: y_1, unit: '1', domain: null, description: First output.}\n - {name: second, symbol: y_2, unit: '1', domain: null, description: Second output.}", + "outputs: {type: scalar, name: value, quantity: volumetric_water_content, symbol: y, unit: volume_fraction, reported_unit: '1', domain: null, description: Test output.}", + "outputs:\n type: record\n fields:\n - {name: first, quantity: volumetric_water_content, symbol: y_1, unit: volume_fraction, reported_unit: '1', domain: null, description: First output.}\n - {name: second, quantity: volumetric_water_content, symbol: y_2, unit: volume_fraction, reported_unit: '1', domain: null, description: Second output.}", ); fs::write( root.join("specs/functions/unnamed_record.yaml"), @@ -667,8 +709,8 @@ functions: "", ) .replace( - "outputs: {type: scalar, name: value, symbol: y, unit: '1', domain: null, description: Test output.}", - "outputs:\n type: record\n name: result_record\n fields:\n - {name: first, symbol: y_1, unit: '1', domain: null, description: First output.}\n - {name: second, symbol: y_2, unit: '1', domain: null, description: Second output.}", + "outputs: {type: scalar, name: value, quantity: volumetric_water_content, symbol: y, unit: volume_fraction, reported_unit: '1', domain: null, description: Test output.}", + "outputs:\n type: record\n name: result_record\n fields:\n - {name: first, quantity: volumetric_water_content, symbol: y_1, unit: volume_fraction, reported_unit: '1', domain: null, description: First output.}\n - {name: second, quantity: volumetric_water_content, symbol: y_2, unit: volume_fraction, reported_unit: '1', domain: null, description: Second output.}", ); fs::write( root.join("specs/functions/non_pascal_case_record.yaml"), @@ -693,10 +735,10 @@ functions: ) .replace( "functions:\n", - "$defs:\n reusable_result:\n type: record\n fields:\n - {name: value, symbol: y, unit: '1', domain: null, description: Test output.}\nfunctions:\n", + "$defs:\n reusable_result:\n type: record\n fields:\n - {name: value, quantity: volumetric_water_content, symbol: y, unit: volume_fraction, reported_unit: '1', domain: null, description: Test output.}\nfunctions:\n", ) .replace( - "outputs: {type: scalar, name: value, symbol: y, unit: '1', domain: null, description: Test output.}", + "outputs: {type: scalar, name: value, quantity: volumetric_water_content, symbol: y, unit: volume_fraction, reported_unit: '1', domain: null, description: Test output.}", "outputs: {$ref: '#/$defs/reusable_result'}", ); fs::write( @@ -777,4 +819,695 @@ functions: assert!(error.contains("filename stem must be an APA-style slug")); } + + fn verification_specification(slug: &str, case: &str) -> String { + specification( + slug, + &format!( + " implementation:\n variables: [{{name: value, expr: x}}]\n verification_cases:\n - id: reference\n inputs: {{x: 1.0}}\n{case}" + ), + "", + ) + } + + #[test] + fn accepts_both_verification_case_kinds() { + let cases = [ + ( + "published", + " expected: {value: 1.0}\n kind: published\n source_location: Table 1, row 1\n notes: Source lookup.\n", + ), + ( + "calculated", + " expected: {value: 1.0}\n kind: calculated\n source_location: Table 1, input row\n rationale: Interior representative input.\n", + ), + ]; + for (slug, case) in cases { + let root = fixture_root(slug); + fs::write( + root.join(format!("specs/functions/{slug}.yaml")), + verification_specification(slug, case), + ) + .unwrap(); + load(&root).unwrap_or_else(|error| panic!("{slug} case failed: {error}")); + fs::remove_dir_all(root).unwrap(); + } + } + + #[test] + fn requires_verification_cases_for_code_generating_statuses() { + for status in ["ready-for-implementation", "implemented"] { + let slug = status.replace('-', "_"); + let root = fixture_root(&slug); + let text = specification( + &slug, + " implementation:\n variables: [{name: value, expr: x}]\n", + "", + ) + .replace( + " verification_cases:\n - {id: reference, kind: calculated, inputs: {x: 1.0}, expected: {value: 1.0}, rationale: Test fixture.}\n", + "", + ) + .replace("status: ready-for-implementation", &format!("status: {status}")); + fs::write(root.join(format!("specs/functions/{slug}.yaml")), text).unwrap(); + + let error = load(&root) + .expect_err("code-generating status without cases must fail") + .to_string(); + assert!(error.contains("verification_cases"), "{error}"); + fs::remove_dir_all(root).unwrap(); + } + } + + #[test] + fn permits_blocked_functions_without_verification_cases() { + let root = fixture_root("blocked_without_cases"); + let text = specification("blocked_without_cases", "", "") + .replace("status: ready-for-implementation", "status: blocked"); + fs::write( + root.join("specs/functions/blocked_without_cases.yaml"), + text, + ) + .unwrap(); + + load(&root).expect("blocked function may omit cases"); + fs::remove_dir_all(root).unwrap(); + } + + #[test] + fn rejects_duplicate_verification_case_ids_within_a_function() { + let root = fixture_root("duplicate_case_id"); + let text = verification_specification( + "duplicate_case_id", + " expected: {value: 1.0}\n kind: calculated\n rationale: First fixture.\n - {id: reference, kind: calculated, inputs: {x: 2.0}, expected: {value: 2.0}, rationale: Second fixture.}\n", + ); + fs::write(root.join("specs/functions/duplicate_case_id.yaml"), text).unwrap(); + + let error = crate::load_validated_specifications(&root) + .expect_err("duplicate case IDs must fail") + .to_string(); + assert!(error.contains("duplicate id `reference`"), "{error}"); + fs::remove_dir_all(root).unwrap(); + } + + #[test] + fn rejects_absolute_tolerance_that_masks_a_nonzero_expected_value() { + let root = fixture_root("nondiscriminating_tolerance"); + let text = specification( + "nondiscriminating_tolerance", + " verification_tolerances:\n value: {absolute: 1.0, source_location: Test policy}\n implementation:\n variables: [{name: value, expr: x}]\n", + "", + ); + fs::write( + root.join("specs/functions/nondiscriminating_tolerance.yaml"), + text, + ) + .unwrap(); + + let error = crate::load_validated_specifications(&root) + .and_then(crate::compile::functions) + .expect_err("non-discriminating tolerance must fail") + .to_string(); + assert!( + error.contains("greater than or equal to the magnitude"), + "{error}" + ); + fs::remove_dir_all(root).unwrap(); + } + + #[test] + fn rejects_relative_tolerance_that_masks_a_nonzero_expected_value() { + let root = fixture_root("nondiscriminating_relative_tolerance"); + let text = specification( + "nondiscriminating_relative_tolerance", + " verification_tolerances:\n value: {absolute: 0.001, relative: 1.0, source_location: Test policy}\n implementation:\n variables: [{name: value, expr: x}]\n", + "", + ); + fs::write( + root.join("specs/functions/nondiscriminating_relative_tolerance.yaml"), + text, + ) + .unwrap(); + + let error = crate::load_validated_specifications(&root) + .and_then(crate::compile::functions) + .expect_err("non-discriminating relative tolerance must fail") + .to_string(); + assert!( + error.contains("greater than or equal to the magnitude"), + "{error}" + ); + fs::remove_dir_all(root).unwrap(); + } + + #[test] + fn rejects_invalid_verification_case_provenance() { + for (slug, case) in [ + ( + "unknown_kind", + " expected: {value: 1.0}\n kind: external\n rationale: Invalid kind.\n", + ), + ( + "published_without_location", + " expected: {value: 1.0}\n kind: published\n", + ), + ( + "calculated_without_rationale", + " expected: {value: 1.0}\n kind: calculated\n", + ), + ] { + let root = fixture_root(slug); + fs::write( + root.join(format!("specs/functions/{slug}.yaml")), + verification_specification(slug, case), + ) + .unwrap(); + let error = load(&root) + .expect_err("invalid verification case must fail") + .to_string(); + assert!( + error.contains("Additional properties are not allowed") + || error.contains("required property") + || error.contains("not valid under any of the schemas"), + "{error}" + ); + fs::remove_dir_all(root).unwrap(); + } + } + + #[test] + fn rejects_missing_and_unknown_expected_outputs() { + for (slug, expected) in [ + ("missing_expected", ""), + ("unknown_expected", " expected: {other: 1.0}\n"), + ] { + let root = fixture_root(slug); + fs::write( + root.join(format!("specs/functions/{slug}.yaml")), + verification_specification( + slug, + &format!( + "{expected} kind: calculated\n rationale: Interior representative input.\n" + ), + ), + ) + .unwrap(); + let result = load(&root).and_then(crate::compile::functions); + let error = result + .expect_err("invalid expected outputs must fail") + .to_string(); + assert!( + error.contains("missing expected output") + || error.contains("references unknown output") + || error.contains("required property") + || error.contains("missing field `expected`"), + "{error}" + ); + fs::remove_dir_all(root).unwrap(); + } + } + + #[test] + fn resolves_registry_defaults_and_replacing_source_overrides() { + let root = fixture_root("tolerance-resolution"); + let default_spec = specification( + "default_tolerance", + " implementation:\n variables: [{name: value, expr: x}]\n", + "", + ); + fs::write( + root.join("specs/functions/default_tolerance.yaml"), + default_spec, + ) + .unwrap(); + let override_spec = specification( + "override_tolerance", + " verification_tolerances:\n value:\n absolute: 0.005\n relative: 0.01\n source_location: Table 5\n implementation:\n variables: [{name: value, expr: x}]\n", + "", + ); + fs::write( + root.join("specs/functions/override_tolerance.yaml"), + override_spec, + ) + .unwrap(); + + let compiled = crate::compile::functions( + crate::load_validated_specifications(&root).expect("fixtures validate"), + ) + .unwrap(); + let default = &compiled[0].output_tolerances[0]; + assert_eq!(default.absolute, 0.001); + assert_eq!(default.relative, 0.0); + assert!(matches!( + default.source, + crate::model::ToleranceSource::Registry + )); + let override_ = &compiled[1].output_tolerances[0]; + assert_eq!(override_.absolute, 0.005); + assert_eq!(override_.relative, 0.01); + assert!(matches!( + &override_.source, + crate::model::ToleranceSource::SourceOverride(location) if location == "Table 5" + )); + fs::remove_dir_all(root).unwrap(); + } + + #[test] + fn rejects_unknown_verification_inputs() { + let root = fixture_root("unknown-verification-input"); + let text = verification_specification( + "unknown_verification_input", + " expected: {value: 1.0}\n kind: calculated\n rationale: Interior input.\n", + ) + .replace("inputs: {x: 1.0}", "inputs: {x: 1.0, stale: 2.0}"); + fs::write( + root.join("specs/functions/unknown_verification_input.yaml"), + text, + ) + .unwrap(); + let error = crate::load_validated_specifications(&root) + .and_then(crate::compile::functions) + .expect_err("unknown input must fail") + .to_string(); + assert!(error.contains("unknown input `stale`"), "{error}"); + fs::remove_dir_all(root).unwrap(); + } + + #[test] + fn rejects_missing_verification_inputs() { + let root = fixture_root("missing-verification-input"); + let text = verification_specification( + "missing_verification_input", + " expected: {value: 1.0}\n kind: calculated\n rationale: Interior input.\n", + ) + .replace( + " - {name: x, symbol: x, unit: '1', domain: null, description: Test input.}", + " - {name: x, symbol: x, unit: '1', domain: null, description: Test input.}\n - {name: y, symbol: y, unit: '1', domain: null, description: Missing test input.}", + ); + fs::write( + root.join("specs/functions/missing_verification_input.yaml"), + text, + ) + .unwrap(); + let error = crate::load_validated_specifications(&root) + .and_then(crate::compile::functions) + .expect_err("missing input must fail") + .to_string(); + assert!(error.contains("missing input `y`"), "{error}"); + fs::remove_dir_all(root).unwrap(); + } + + #[test] + fn keeps_overrides_function_local_for_shared_outputs() { + let root = fixture_root("shared-output-overrides"); + let text = r#"source: + summary: Test source. + citation_apa: Test (2026). + doi: null +$defs: + SharedResult: + type: record + name: SharedResult + fields: + - {name: value, quantity: volumetric_water_content, symbol: y, unit: volume_fraction, reported_unit: '1', domain: null, description: Test output.} + - {name: second, quantity: volumetric_water_content, symbol: z, unit: volume_fraction, reported_unit: '1', domain: null, description: Second test output.} +functions: + - name: calc_ptf_shared_default + status: ready-for-implementation + public_api: {name: calc_ptf_shared_default, result_class: SharedResult, summary: Test value.} + scope: + prediction_target: Test value. + models: {h_theta: null, k_h: null} + inputs: + - {name: x, symbol: x, unit: '1', domain: null, description: Test input.} + outputs: {$ref: '#/$defs/SharedResult'} + implementation: + variables: [{name: value, expr: x}, {name: second, expr: x}] + verification_cases: + - {id: default, kind: calculated, inputs: {x: 1.0}, expected: {value: 1.0, second: 1.0}, rationale: Test fixture.} + - name: calc_ptf_shared_override + status: ready-for-implementation + public_api: {name: calc_ptf_shared_override, result_class: SharedResult, summary: Test value.} + scope: + prediction_target: Test value. + models: {h_theta: null, k_h: null} + inputs: + - {name: x, symbol: x, unit: '1', domain: null, description: Test input.} + outputs: {$ref: '#/$defs/SharedResult'} + verification_tolerances: + value: {absolute: 0.005, source_location: Table 5} + implementation: + variables: [{name: value, expr: x}, {name: second, expr: x}] + verification_cases: + - {id: override, kind: calculated, inputs: {x: 1.0}, expected: {value: 1.0, second: 1.0}, rationale: Test fixture.} +"#; + fs::write( + root.join("specs/functions/shared_output_overrides.yaml"), + text, + ) + .unwrap(); + + let compiled = crate::load_validated_specifications(&root) + .and_then(crate::compile::functions) + .expect("shared output definitions must compile"); + assert_eq!(compiled[0].output_tolerances[0].absolute, 0.001); + assert_eq!(compiled[1].output_tolerances[0].absolute, 0.005); + fs::remove_dir_all(root).unwrap(); + } + + #[test] + fn rejects_missing_unknown_and_unregistered_output_quantities() { + for (label, replacement, expected) in [ + ( + "missing-quantity", + "name: value, symbol: y", + "not valid under any of the schemas", + ), + ( + "unknown-quantity", + "quantity: unknown_quantity", + "unknown quantity", + ), + ( + "unregistered-unit", + "unit: 'unregistered'", + "no registered unit", + ), + ] { + let root = fixture_root(label); + let mut text = specification( + &label.replace('-', "_"), + " implementation:\n variables: [{name: value, expr: x}]\n", + "", + ); + text = match label { + "missing-quantity" => text.replace( + "name: value, quantity: volumetric_water_content, symbol: y", + replacement, + ), + "unknown-quantity" => { + text.replace("quantity: volumetric_water_content", replacement) + } + _ => text.replace("unit: volume_fraction", replacement), + }; + fs::write( + root.join(format!("specs/functions/{}.yaml", label.replace('-', "_"))), + text, + ) + .unwrap(); + let error = crate::load_validated_specifications(&root) + .expect_err("invalid quantity contract must fail") + .to_string(); + assert!(error.contains(expected), "{error}"); + fs::remove_dir_all(root).unwrap(); + } + } + + #[test] + fn rejects_invalid_and_unknown_tolerance_overrides() { + for (label, tolerance, expected) in [ + ( + "missing-location", + " verification_tolerances: {value: {absolute: 0.1}}\n", + "required property", + ), + ( + "unknown-output", + " verification_tolerances: {other: {absolute: 0.1, source_location: Table 1}}\n", + "unknown output", + ), + ( + "zero-absolute", + " verification_tolerances: {value: {absolute: 0.0, source_location: Table 1}}\n", + "less than or equal to the minimum", + ), + ] { + let root = fixture_root(label); + let text = specification( + &label.replace('-', "_"), + &format!( + "{tolerance} implementation:\n variables: [{{name: value, expr: x}}]\n" + ), + "", + ); + fs::write( + root.join(format!("specs/functions/{}.yaml", label.replace('-', "_"))), + text, + ) + .unwrap(); + let error = crate::load_validated_specifications(&root) + .expect_err("invalid override must fail") + .to_string(); + assert!(error.contains(expected), "{error}"); + fs::remove_dir_all(root).unwrap(); + } + } + + #[test] + fn rejects_invalid_registry_tolerances_and_duplicate_identifiers() { + for (label, edit, expected) in [ + ( + "zero", + "zero", + "absolute tolerance must be finite and positive", + ), + ( + "negative_relative", + "negative_relative", + "relative tolerance must be finite and non-negative", + ), + ( + "duplicate_identifier", + "duplicate_identifier", + "duplicate entry", + ), + ("duplicate_unit", "duplicate_unit", "duplicate entry"), + ] { + let root = fixture_root(label); + write( + &root, + label, + " implementation:\n variables: [{name: value, expr: x}]\n", + "", + ); + let registry_path = root.join("specs/quantities.yaml"); + let mut registry = fs::read_to_string(®istry_path) + .unwrap() + .replace("\r\n", "\n"); + match edit { + "zero" => { + registry = registry.replacen("absolute: 0.001", "absolute: 0.0", 1); + } + "negative_relative" => { + registry = registry.replacen("relative: 0.01", "relative: -0.01", 1); + } + "duplicate_identifier" => registry.push_str( + "\nvolumetric_water_content:\n description: Duplicate.\n units:\n '1':\n absolute: 0.1\n rationale: Duplicate.\n", + ), + _ => registry = registry.replacen( + " volume_fraction:\n absolute: 0.001", + " volume_fraction:\n absolute: 0.001\n rationale: First.\n volume_fraction:\n absolute: 0.002", + 1, + ), + } + fs::write(registry_path, registry).unwrap(); + let error = format!( + "{:#}", + crate::load_validated_specifications(&root) + .expect_err("invalid registry must fail") + ); + assert!(error.contains(expected), "{error}"); + fs::remove_dir_all(root).unwrap(); + } + } + #[test] + fn aliases_preserve_values_and_quantity_specific_tolerances() { + for (unit, notation, tolerance) in [ + ("volume_percent", "vol.%", 0.1), + ("volume_percent", "% v/v", 0.1), + ("volume_percent", "% volume/volume", 0.1), + ("volume_percent", "%", 0.1), + ("volume_fraction", "cm\u{00b3}/cm\u{00b3}", 0.001), + ("volume_fraction", "cm^3/cm^3", 0.001), + ] { + let root = fixture_root("unit-alias"); + let text = specification("alias", " implementation:\n variables: [{name: value, expr: x}]\n verification_cases:\n - {id: unchanged, kind: calculated, inputs: {x: 42.0}, expected: {value: 42.0}, rationale: Unchanged source value.}\n", "") + .replace("unit: volume_fraction, reported_unit: '1'", &format!("unit: {unit}, reported_unit: '{notation}'")); + fs::write(root.join("specs/functions/alias.yaml"), text).unwrap(); + let entries = crate::load_validated_specifications(&root).unwrap(); + let compiled = crate::compile::functions(entries).unwrap(); + assert_eq!(compiled[0].verification_cases[0].expected, vec![42.0]); + assert_eq!(compiled[0].output_tolerances[0].absolute, tolerance); + assert_eq!(compiled[0].output_tolerances[0].unit, unit); + fs::remove_dir_all(root).unwrap(); + } + } + + #[test] + fn rejects_unit_conversion_and_wrong_quantity_context() { + for (unit, notation, expected) in [ + ("volume_fraction", "% v/v", "not an equivalent notation"), + ("volume_percent", "m^3/m^3", "not an equivalent notation"), + ("millimeter_per_hour", "cm/h", "not an equivalent notation"), + ("kilopascal", "cm H2O", "not an equivalent notation"), + ("mass_percent", "%", "no registered unit"), + ("dimensionless", "1", "no registered unit"), + ("unknown", "1", "unknown unit identifier"), + ] { + let root = fixture_root("invalid-unit"); + let text = specification( + "invalid_unit", + " implementation:\n variables: [{name: value, expr: x}]\n", + "", + ) + .replace( + "unit: volume_fraction, reported_unit: '1'", + &format!("unit: {unit}, reported_unit: '{notation}'"), + ); + fs::write(root.join("specs/functions/invalid_unit.yaml"), text).unwrap(); + let error = crate::load_validated_specifications(&root) + .unwrap_err() + .to_string(); + assert!(error.contains(expected), "{error}"); + fs::remove_dir_all(root).unwrap(); + } + } + #[test] + fn rejects_invalid_unit_registry_contracts() { + for (label, extra, expected) in [ + ( + "duplicate", + "volume_fraction: {preferred_notation: other}\n", + "duplicate entry", + ), + ( + "invalid_id", + "Bad-id: {preferred_notation: other}\n", + "invalid unit identifier", + ), + ( + "empty", + "empty: {preferred_notation: ''}\n", + "empty or duplicate notation", + ), + ( + "duplicate_alias", + "duplicate_alias: {preferred_notation: x, aliases: [x]}\n", + "empty or duplicate notation", + ), + ( + "conversion", + "conversion: {preferred_notation: x, scale: 100}\n", + "unknown field", + ), + ( + "tolerance", + "tolerance: {preferred_notation: x, absolute: 0.1}\n", + "unknown field", + ), + ] { + let root = fixture_root(label); + write( + &root, + label, + " implementation:\n variables: [{name: value, expr: x}]\n", + "", + ); + let path = root.join("specs/units.yaml"); + let mut registry = fs::read_to_string(&path).unwrap(); + registry.push_str(extra); + fs::write(path, registry).unwrap(); + let error = format!( + "{:#}", + crate::load_validated_specifications(&root).unwrap_err() + ); + assert!(error.contains(expected), "{error}"); + fs::remove_dir_all(root).unwrap(); + } + } + + #[test] + fn shared_unit_keeps_quantity_specific_tolerances() { + let root = fixture_root("shared-unit-tolerances"); + let path = root.join("specs/quantities.yaml"); + let mut registry = fs::read_to_string(&path).unwrap(); + registry.push_str("\nother_fraction:\n description: Test distinct quantity.\n units:\n volume_fraction:\n absolute: 0.25\n rationale: Test quantity-specific resolution.\n"); + fs::write(path, registry).unwrap(); + for quantity in ["volumetric_water_content", "other_fraction"] { + let text = specification( + quantity, + " implementation:\n variables: [{name: value, expr: x}]\n", + "", + ) + .replace( + "quantity: volumetric_water_content", + &format!("quantity: {quantity}"), + ); + fs::write(root.join(format!("specs/functions/{quantity}.yaml")), text).unwrap(); + } + let compiled = + crate::compile::functions(crate::load_validated_specifications(&root).unwrap()) + .unwrap(); + assert_eq!(compiled[0].output_tolerances[0].absolute, 0.25); + assert_eq!(compiled[1].output_tolerances[0].absolute, 0.001); + assert_eq!( + compiled[0].output_tolerances[0].unit, + compiled[1].output_tolerances[0].unit + ); + let path = root.join("specs/quantities.yaml"); + let registry = fs::read_to_string(&path) + .unwrap() + .replace(" volume_fraction:", " missing_unit:"); + fs::write(path, registry).unwrap(); + assert!( + crate::load_validated_specifications(&root) + .unwrap_err() + .to_string() + .contains("unknown unit identifier") + ); + fs::remove_dir_all(root).unwrap(); + } + + #[test] + fn rejects_ambiguous_notation_within_a_quantity() { + let root = fixture_root("ambiguous-notation"); + write( + &root, + "ambiguous", + " implementation:\n variables: [{name: value, expr: x}]\n", + "", + ); + let path = root.join("specs/units.yaml"); + let registry = fs::read_to_string(&path) + .unwrap() + .replace("aliases: []", "aliases: [\"%\"]"); + fs::write(path, registry).unwrap(); + assert!( + crate::load_validated_specifications(&root) + .unwrap_err() + .to_string() + .contains("ambiguous notation") + ); + fs::remove_dir_all(root).unwrap(); + } + #[test] + fn requires_normalized_and_reported_output_units() { + for missing in ["unit: volume_fraction, ", "reported_unit: '1', "] { + let root = fixture_root("missing-unit-metadata"); + let text = specification( + "missing_unit", + " implementation:\n variables: [{name: value, expr: x}]\n", + "", + ) + .replace(missing, ""); + fs::write(root.join("specs/functions/missing_unit.yaml"), text).unwrap(); + assert!( + load(&root) + .unwrap_err() + .to_string() + .contains("not valid under any of the schemas") + ); + fs::remove_dir_all(root).unwrap(); + } + } } diff --git a/codegen/src/targets/catalog.rs b/codegen/src/targets/catalog.rs index b142d4b..b60246c 100644 --- a/codegen/src/targets/catalog.rs +++ b/codegen/src/targets/catalog.rs @@ -1,6 +1,6 @@ use crate::{ documentation::{self as docs, FunctionDocument}, - model::{Entry, Parameter}, + model::{Entry, OutputField, Parameter}, output::GeneratedFile, render::{Render, Writer, markdown}, }; @@ -144,7 +144,7 @@ impl Render for FunctionSection<'_> { trait CatalogParameter { fn name(&self) -> &str; - fn unit(&self) -> &str; + fn unit_notation(&self) -> &str; fn domain(&self) -> Option<&str>; fn description(&self) -> &str; } @@ -153,7 +153,7 @@ impl CatalogParameter for Parameter { fn name(&self) -> &str { &self.name } - fn unit(&self) -> &str { + fn unit_notation(&self) -> &str { &self.unit } fn domain(&self) -> Option<&str> { @@ -164,6 +164,21 @@ impl CatalogParameter for Parameter { } } +impl CatalogParameter for OutputField { + fn name(&self) -> &str { + &self.name + } + fn unit_notation(&self) -> &str { + &self.reported_unit + } + fn domain(&self) -> Option<&str> { + self.domain.as_deref() + } + fn description(&self) -> &str { + &self.description + } +} + fn render_input_table(writer: &mut Writer, inputs: &[crate::model::Input]) { writer.write( "#### Inputs\n\n| Name | Type | Unit | Domain | Description |\n| --- | --- | --- | --- | --- |\n", @@ -199,7 +214,7 @@ fn render_parameter_table(writer: &mut Writer, title: &str, parameters: &[impl C writer.line(format_args!( "| `{}` | {} | {} | {} |", parameter.name(), - escape_table(parameter.unit()), + escape_table(parameter.unit_notation()), parameter .domain() .map(escape_table) diff --git a/codegen/src/targets/mod.rs b/codegen/src/targets/mod.rs index 160a6db..211ec0c 100644 --- a/codegen/src/targets/mod.rs +++ b/codegen/src/targets/mod.rs @@ -113,6 +113,7 @@ pub(crate) fn run(root: &Path, entries: Vec) -> Result<()> { Output::new(&output::PYTHON_TEST, python.tests), Output::new(&output::NATIVE_C, native.c_headers), Output::new(&output::NATIVE_CPP_MODULE, native.cpp_modules), + Output::new(&output::NATIVE_TEST_SUPPORT, native.test_support), Output::new(&output::NATIVE_C_TEST, native.c_tests), Output::new(&output::NATIVE_CPP_TEST, native.cpp_tests), ], diff --git a/codegen/src/targets/native.rs b/codegen/src/targets/native.rs index e97fd45..f5ed920 100644 --- a/codegen/src/targets/native.rs +++ b/codegen/src/targets/native.rs @@ -6,7 +6,8 @@ use convert_case::{Boundary, Case, Casing}; use crate::{ documentation::{self as docs, FunctionDocument, SourceDocument}, model::{ - CompiledFunction, CompiledInput, EnumDefinition, Function, Output, Parameter, Scope, Source, + CompiledFunction, CompiledInput, EnumDefinition, Function, Output, OutputField, Scope, + Source, }, render::{Render, Writer}, semantic::{RecordLookup, Reference, ResultBinding, VariableValue}, @@ -25,13 +26,15 @@ pub(super) struct OutputFiles { pub(super) cpp_modules: Vec, pub(super) c_tests: Vec, pub(super) cpp_tests: Vec, + pub(super) test_support: Vec, } pub(super) fn render(functions: &[CompiledFunction]) -> Result { let mut c_headers = Vec::new(); let mut cpp_modules = Vec::new(); - let mut c_tests = Vec::new(); - let mut cpp_tests = Vec::new(); + let mut c_tests = vec![file("comparator.c", comparator_test(false))]; + let mut cpp_tests = vec![file("comparator.cpp", comparator_test(true))]; + let test_support = vec![file("close_enough.h", close_enough_header())]; let mut umbrella = Writer::new(); umbrella.write(format_args!( "{HEADER}\n\n#ifndef PTFKIT_PTFKIT_H\n#define PTFKIT_PTFKIT_H\n\n" @@ -63,6 +66,7 @@ pub(super) fn render(functions: &[CompiledFunction]) -> Result { cpp_modules, c_tests, cpp_tests, + test_support, }) } @@ -682,7 +686,7 @@ fn function_comment_from_document(document: FunctionDocument<'_>) -> Comment { Comment(lines) } -fn field_comment(parameter: &Parameter) -> Comment { +fn field_comment(parameter: &OutputField) -> Comment { Comment(vec![format!( "@brief {}", docs::parameter_details(parameter) @@ -768,13 +772,13 @@ fn cpp_test(slug: &str, functions: &[&CompiledFunction]) -> Result { fn c_compatibility_test(slug: &str, functions: &[&CompiledFunction]) -> Result { let mut writer = Writer::new(); writer.write(format_args!( - "{HEADER}\n\n#include \n#include \"close_enough.h\"\n\n" + "{HEADER}\n\n#include \n#include \"support/close_enough.h\"\n\n" )); writer.line("int main() {"); writer.indented(|writer| { for function in functions { let spec = &function.entry.spec.functions[function.function_index]; - for case in &function.golden_tests { + for case in &function.verification_cases { writer.line("{"); writer.indented(|writer| { if matches!(function.core.output, Output::Scalar) { @@ -790,8 +794,14 @@ fn c_compatibility_test(slug: &str, functions: &[&CompiledFunction]) -> Result Result Result { .iter() .any(|function| matches!(function.core.output, Output::Struct(_))); let mut writer = Writer::new(); - writer.write(format_args!("{HEADER}\n\n#ifdef IMPORT_UMBRELLA\nimport ptfkit;\n#else\nimport ptfkit.{slug};\n#endif\n\n#include \"close_enough.h\"")); + writer.write(format_args!("{HEADER}\n\n#ifdef IMPORT_UMBRELLA\nimport ptfkit;\n#else\nimport ptfkit.{slug};\n#endif\n\n#include \"support/close_enough.h\"")); if type_traits { writer.write("\n#include \n\n"); } else { @@ -830,7 +839,7 @@ fn module_test(slug: &str, functions: &[&CompiledFunction]) -> Result { writer.indented(|writer| { for function in functions { let spec = &function.entry.spec.functions[function.function_index]; - for case in &function.golden_tests { + for case in &function.verification_cases { writer.line("{"); writer.indented(|writer| { writer.write(format_args!("const auto result = ptfkit::{slug}::{}(", function.core.name)); @@ -842,8 +851,14 @@ fn module_test(slug: &str, functions: &[&CompiledFunction]) -> Result { .expect("record output has a result class"); writer.line(format_args!("static_assert(std::is_same_v, ptfkit::{slug}::{result}>);")); } - for (field, expected) in spec.outputs.fields().iter().zip(&case.expected) { - writer.write("assert_close_enough("); + for ((field, expected), tolerance) in spec + .outputs + .fields() + .iter() + .zip(&case.expected) + .zip(&function.output_tolerances) + { + writer.write("assert_close("); if matches!(function.core.output, Output::Scalar) { writer.write("result"); } else { @@ -852,9 +867,8 @@ fn module_test(slug: &str, functions: &[&CompiledFunction]) -> Result { writer.write(", "); writer.write(c::test_float_literal(*expected)); writer.write(", "); - writer.write(c::test_float_literal(case.atol)); - writer.write(", "); - writer.write(c::test_float_literal(case.rtol)); + write_tolerance_arguments(writer, tolerance); + writer.write(format_args!(", {:?}", case.id)); writer.line(");"); } }); @@ -867,6 +881,121 @@ fn module_test(slug: &str, functions: &[&CompiledFunction]) -> Result { Ok(writer.into_string()) } +fn write_tolerance_arguments(writer: &mut Writer, tolerance: &crate::model::CompiledTolerance) { + let source = match &tolerance.source { + crate::model::ToleranceSource::Registry => "registry", + crate::model::ToleranceSource::SourceOverride(_) => "source override", + }; + writer.write(c::test_float_literal(tolerance.absolute)); + writer.write(", "); + writer.write(c::test_float_literal(tolerance.relative)); + writer.write(format_args!( + ", \"{}\", \"{}\", \"{}\"", + tolerance.quantity, tolerance.unit, source + )); +} + +fn close_enough_header() -> String { + let guard = c::test_float_literal(crate::compile::FLOATING_POINT_GUARD); + format!( + r#"{HEADER} + +#ifndef PTFKIT_TEST_CLOSE_ENOUGH_H +#define PTFKIT_TEST_CLOSE_ENOUGH_H + +#ifdef __cplusplus +#include +#include +#include +#include +#include + +inline double resolved_tolerance(double expected, double absolute, double relative) {{ + return std::max({{absolute, relative * std::abs(expected), {guard}}}); +}} + +inline bool is_close(double actual, double expected, double absolute, double relative) {{ + return std::abs(actual - expected) <= resolved_tolerance(expected, absolute, relative); +}} + +inline void _close_enough_impl(const char *file, int line, double actual, double expected, + double absolute, double relative, const char *quantity, + const char *unit, const char *source, const char *case_id) {{ + const double tolerance = resolved_tolerance(expected, absolute, relative); + const double difference = std::abs(actual - expected); + if (!is_close(actual, expected, absolute, relative)) {{ + std::println(stderr, "assertion failed: {{}}:{{}}: case={{}}, actual={{}}, expected={{}}, difference={{}}, tolerance={{}}, quantity={{}}, unit={{}}, source={{}}", + file, line, case_id, actual, expected, difference, tolerance, quantity, unit, source); + std::exit(EXIT_FAILURE); + }} +}} + +#else +#include +#include +#include + +static inline double resolved_tolerance(double expected, double absolute, double relative) {{ + return fmax(fmax(absolute, relative * fabs(expected)), {guard}); +}} + +static inline int is_close(double actual, double expected, double absolute, double relative) {{ + return fabs(actual - expected) <= resolved_tolerance(expected, absolute, relative); +}} + +static inline void _close_enough_impl(const char *file, int line, double actual, double expected, + double absolute, double relative, const char *quantity, + const char *unit, const char *source, const char *case_id) {{ + const double tolerance = resolved_tolerance(expected, absolute, relative); + const double difference = fabs(actual - expected); + if (!is_close(actual, expected, absolute, relative)) {{ + fprintf(stderr, "assertion failed: %s:%d: case=%s, actual=%.17g, expected=%.17g, difference=%.17g, tolerance=%.17g, quantity=%s, unit=%s, source=%s\n", + file, line, case_id, actual, expected, difference, tolerance, quantity, unit, source); + exit(EXIT_FAILURE); + }} +}} +#endif + +#define assert_close(actual, expected, absolute, relative, quantity, unit, source, case_id) \ + do {{ \ + _close_enough_impl(__FILE__, __LINE__, (actual), (expected), (absolute), (relative), \ + (quantity), (unit), (source), (case_id)); \ + }} while (0) + +#endif +"# + ) +} + +fn comparator_test(cpp: bool) -> String { + let main = if cpp { "int main()" } else { "int main(void)" }; + format!( + r#"{HEADER} + +#include "support/close_enough.h" + +{main} {{ + const double absolute = 0.001; + const double relative = 0.01; + const double expected_values[] = {{0.0, 2.0, -2.0}}; + for (int index = 0; index < 3; ++index) {{ + const double expected = expected_values[index]; + const double tolerance = resolved_tolerance(expected, absolute, relative); + _close_enough_impl(__FILE__, __LINE__, expected + tolerance * 0.5, expected, absolute, + relative, "test_quantity", "1", "registry", "comparator"); + if (is_close(expected + tolerance * 2.0, expected, absolute, relative)) {{ + return EXIT_FAILURE; + }} + }} + if (is_close(NAN, 1.0, absolute, relative)) {{ + return EXIT_FAILURE; + }} + return EXIT_SUCCESS; +}} +"# + ) +} + fn render_literals( writer: &mut Writer, values: &[CompiledInput], diff --git a/codegen/src/targets/python/test.rs b/codegen/src/targets/python/test.rs index 40b415d..432180f 100644 --- a/codegen/src/targets/python/test.rs +++ b/codegen/src/targets/python/test.rs @@ -1,7 +1,7 @@ use std::collections::BTreeMap; use crate::{ - model::{CompiledFunction, Function, GoldenInput, Outputs, PythonGeneration}, + model::{CompiledFunction, Function, Outputs, PythonGeneration, VerificationInput}, output::GeneratedFile, }; @@ -16,19 +16,136 @@ pub(super) fn render(functions: &[CompiledFunction]) -> Vec { .push(function); } - modules - .into_iter() - .filter_map(|(slug, functions)| { - (functions[0].entry.spec.generation.public_python == PythonGeneration::Generated).then( - || { - GeneratedFile::new( - format!("tests/test_{slug}.py").into(), - module_source(&slug, &functions), - ) - }, - ) - }) - .collect() + let mut files = vec![ + GeneratedFile::new("tests/_helpers.py".into(), helper_source()), + GeneratedFile::new("tests/test_comparator.py".into(), comparator_test_source()), + ]; + files.extend( + modules + .into_iter() + .filter_map(|(slug, functions)| { + (functions[0].entry.spec.generation.public_python == PythonGeneration::Generated) + .then(|| { + GeneratedFile::new( + format!("tests/test_{slug}.py").into(), + module_source(&slug, &functions), + ) + }) + }) + .collect::>(), + ); + files +} + +fn comparator_test_source() -> String { + format!( + r#"{WRAPPER_HEADER} +import pytest + +from _helpers import assert_close, resolved_tolerance + + +@pytest.mark.parametrize('expected', [0.0, 2.0, -2.0]) +def test_accepts_below_and_rejects_above_tolerance(expected: float): + absolute = 0.001 + relative = 0.01 + tolerance = resolved_tolerance(expected, absolute, relative) + metadata = {{ + 'absolute': absolute, + 'relative': relative, + 'quantity': 'test_quantity', + 'unit': '1', + 'source': 'registry', + }} + assert_close(expected + tolerance * 0.5, expected, **metadata) + with pytest.raises(AssertionError): + assert_close(expected + tolerance * 2.0, expected, **metadata) +"# + ) +} + +fn helper_source() -> String { + format!( + r#"{WRAPPER_HEADER} +from __future__ import annotations + +from enum import Enum +from typing import TYPE_CHECKING, NamedTuple, TypeVar, overload + +import numpy as np + +from ptfkit.enums import EnumArray + + +if TYPE_CHECKING: + from collections.abc import Mapping, Sequence + from typing import Any + + R = TypeVar('R', bound=NamedTuple) + Expected = dict[str, float] + VerificationCase = tuple[Mapping[str, Any], Expected] + VectorCasePart = tuple[dict[str, Any], Expected] + VectorCaseScalar = tuple[*VectorCasePart, np.ndarray] + VectorCaseTuple = tuple[*VectorCasePart, R] + + +@overload +def prepare_vector_case(cases: Sequence[VerificationCase]) -> VectorCaseScalar: ... + + +@overload +def prepare_vector_case( + cases: Sequence[VerificationCase], result_type: type[R] +) -> VectorCaseTuple: ... + + +def prepare_vector_case( + cases: Sequence[VerificationCase], + result_cls: type[R] | None = None, +) -> VectorCaseScalar | VectorCaseTuple: + inputs, expected = cases[0] + vector_inputs = {{ + name: ( + EnumArray._from_members(type(value), [value]) # noqa: SLF001 + if isinstance(value, Enum) + else np.array([value]) + ) + for name, value in inputs.items() + }} + out: np.ndarray | R + if result_cls is None: + out = np.empty(1, dtype=float) + else: + field_count = len(result_cls._fields) + out = result_cls(*(np.empty(1, dtype=float) for _ in range(field_count))) + return vector_inputs, expected, out + + +def assert_close( + actual: object, + expected: float, + *, + absolute: float, + relative: float, + quantity: str, + unit: str, + source: str, +) -> None: + actual_float = float(actual) # ty: ignore[invalid-argument-type] + tolerance = resolved_tolerance(expected, absolute, relative) + difference = abs(actual_float - expected) + assert difference <= tolerance, ( + f'actual={{actual_float}}, expected={{expected}}, difference={{difference}}, ' + f'tolerance={{tolerance}}, quantity={{quantity!r}}, unit={{unit!r}}, source={{source}}' + ) + + +def resolved_tolerance(expected: float, absolute: float, relative: float) -> float: + scientific_tolerance = max(absolute, relative * abs(expected)) + return max(scientific_tolerance, {:?}) +"#, + crate::compile::FLOATING_POINT_GUARD + ) } fn module_source(slug: &str, functions: &[&CompiledFunction]) -> String { @@ -52,7 +169,7 @@ fn module_source(slug: &str, functions: &[&CompiledFunction]) -> String { module.blank_line(); module.line("import pytest"); module.blank_line(); - module.import("_helpers", "prepare_vector_case"); + module.import("_helpers", "assert_close, prepare_vector_case"); module.import(&format!("ptfkit.{slug}"), imports.join(", ")); module.blank_line(); module.blank_line(); @@ -61,27 +178,37 @@ fn module_source(slug: &str, functions: &[&CompiledFunction]) -> String { module.blank_line(); module.blank_line(); } - let function = &resolved.entry.spec.functions[resolved.function_index]; - function_source(&mut module, function); + function_source(&mut module, resolved); } module.into_string() } -fn function_source(module: &mut Module, function: &Function) { +fn function_source(module: &mut Module, resolved: &CompiledFunction) { + let function = &resolved.entry.spec.functions[resolved.function_index]; let cases_name = format!("CASES_{}", function.public_api.name.to_ascii_uppercase()); + let case_ids_name = format!("{cases_name}_IDS"); module.assignment(&cases_name, "["); module.indented(|writer| { - for case in &function.golden_tests { + for (case, compiled) in function + .verification_cases + .iter() + .zip(&resolved.verification_cases) + { writer.line(format_args!( - "({}, {}, {}, {}),", + "({}, {}),", dictionary(&case.inputs, function), - numeric_dictionary(&case.expected), - float(case.rtol), - float(case.atol), + expected_dictionary(function, &compiled.expected), )); } }); module.line("]"); + module.assignment(&case_ids_name, "["); + module.indented(|writer| { + for case in &function.verification_cases { + writer.line(format_args!("{:?},", case.id)); + } + }); + module.line("]"); module.blank_line(); module.blank_line(); let name = &function.public_api.name; @@ -95,10 +222,10 @@ fn function_source(module: &mut Module, function: &Function) { "float" }; module.line(format_args!( - "@pytest.mark.parametrize(('inputs', 'expected', 'rtol', 'atol'), {cases_name})" + "@pytest.mark.parametrize(('inputs', 'expected'), {cases_name}, ids={case_ids_name})" )); module.line(format_args!( - "def test_{name}_golden(inputs: dict[str, {input_value_type}], expected: dict[str, float], rtol: float, atol: float):" + "def test_{name}_verification(inputs: dict[str, {input_value_type}], expected: dict[str, float]):" )); module.indented(|writer| { if function @@ -113,14 +240,15 @@ fn function_source(module: &mut Module, function: &Function) { writer.line(format_args!("result = {name}(**inputs)")); } writer.blank_line(); - render_expected_assertion(writer, function, ""); + render_expected_assertion(writer, resolved, ""); }); - if !function.golden_tests.is_empty() { - vector_test_source(module, function, &cases_name); + if !function.verification_cases.is_empty() { + vector_test_source(module, resolved, &cases_name); } } -fn vector_test_source(module: &mut Module, function: &Function, cases_name: &str) { +fn vector_test_source(module: &mut Module, resolved: &CompiledFunction, cases_name: &str) { + let function = &resolved.entry.spec.functions[resolved.function_index]; let result_cls = function .result_class() .map(|result_class| format!(", {result_class}")) @@ -131,41 +259,65 @@ fn vector_test_source(module: &mut Module, function: &Function, cases_name: &str module.line(format_args!("def test_{name}_array():")); module.indented(|writer| { writer.line(format_args!( - "inputs, expected, rtol, atol, _out = prepare_vector_case({cases_name}{result_cls})" + "inputs, expected, _out = prepare_vector_case({cases_name}{result_cls})" )); writer.line(format_args!("result = {name}(**inputs, out=None)")); - render_expected_assertion(writer, function, "[0]"); + render_expected_assertion(writer, resolved, "[0]"); }); module.blank_line(); module.blank_line(); module.line(format_args!("def test_{name}_out():")); module.indented(|writer| { writer.line(format_args!( - "inputs, expected, rtol, atol, out = prepare_vector_case({cases_name}{result_cls})" + "inputs, expected, out = prepare_vector_case({cases_name}{result_cls})" )); writer.line(format_args!("result = {name}(**inputs, out=out)")); render_out_assertion(writer, function); - render_expected_assertion(writer, function, "[0]"); + render_expected_assertion(writer, resolved, "[0]"); }); } -fn render_expected_assertion(writer: &mut crate::render::Writer, function: &Function, index: &str) { +fn render_expected_assertion( + writer: &mut crate::render::Writer, + resolved: &CompiledFunction, + index: &str, +) { + let function = &resolved.entry.spec.functions[resolved.function_index]; match &function.outputs { - Outputs::Scalar { field } => writer.line(format_args!( - "assert result{index} == pytest.approx(expected['{}'], rel=rtol, abs=atol)", - field.name + Outputs::Scalar { field } => writer.line(assertion( + &format!("result{index}"), + field.name.as_str(), + &resolved.output_tolerances[0], )), Outputs::Record { fields, .. } => { - for field in fields { - writer.line(format_args!( - "assert result.{}{index} == pytest.approx(expected['{}'], rel=rtol, abs=atol)", - field.name, field.name + for (field, tolerance) in fields.iter().zip(&resolved.output_tolerances) { + writer.line(assertion( + &format!("result.{}{index}", field.name), + field.name.as_str(), + tolerance, )); } } } } +fn assertion(actual: &str, field: &str, tolerance: &crate::model::CompiledTolerance) -> String { + let source = match &tolerance.source { + crate::model::ToleranceSource::Registry => "registry".to_owned(), + crate::model::ToleranceSource::SourceOverride(location) => { + format!("source override: {location}") + } + }; + format!( + "assert_close({actual}, expected['{field}'], absolute={}, relative={}, quantity={:?}, unit={:?}, source={:?})", + float(tolerance.absolute), + float(tolerance.relative), + tolerance.quantity, + tolerance.unit, + source, + ) +} + fn render_out_assertion(writer: &mut crate::render::Writer, function: &Function) { match &function.outputs { Outputs::Scalar { .. } => writer.line("assert result is out"), @@ -176,19 +328,19 @@ fn render_out_assertion(writer: &mut crate::render::Writer, function: &Function) } } -fn dictionary(values: &BTreeMap, function: &Function) -> String { +fn dictionary(values: &BTreeMap, function: &Function) -> String { let entries = values .iter() .map(|(name, value)| { let value = match value { - GoldenInput::Number(value) => float(*value), - GoldenInput::Enum(member) => { + VerificationInput::Number(value) => float(*value), + VerificationInput::Enum(member) => { let enum_name = function .inputs .iter() .find(|input| input.name() == name) .and_then(|input| input.enum_type()) - .expect("enum golden input has a resolved enum type") + .expect("enum verification input has a resolved enum type") .name .as_str(); format!("{enum_name}.{}", member.to_ascii_uppercase()) @@ -201,10 +353,13 @@ fn dictionary(values: &BTreeMap, function: &Function) -> St format!("{{{entries}}}") } -fn numeric_dictionary(values: &BTreeMap) -> String { - let entries = values +fn expected_dictionary(function: &Function, values: &[f64]) -> String { + let entries = function + .outputs + .fields() .iter() - .map(|(name, value)| format!("'{name}': {}", float(*value))) + .zip(values) + .map(|(field, value)| format!("'{}': {}", field.name, float(*value))) .collect::>() .join(", "); format!("{{{entries}}}") diff --git a/codegen/src/targets/python/wrapper.rs b/codegen/src/targets/python/wrapper.rs index b77a893..848e294 100644 --- a/codegen/src/targets/python/wrapper.rs +++ b/codegen/src/targets/python/wrapper.rs @@ -721,9 +721,10 @@ mod tests { name: "TestResult".into(), fields: Vec::new(), }, + verification_tolerances: Default::default(), documentation: Documentation::default(), implementation: None, - golden_tests: Vec::new(), + verification_cases: Vec::new(), edge_cases: Vec::new(), } } diff --git a/codegen/src/targets/reference/c.rs b/codegen/src/targets/reference/c.rs index 1308f57..242edb2 100644 --- a/codegen/src/targets/reference/c.rs +++ b/codegen/src/targets/reference/c.rs @@ -5,7 +5,7 @@ use anyhow::Result; use crate::render::markdown::HEADER; use crate::{ documentation::{self as docs}, - model::{CompiledFunction, EnumDefinition, Output, Parameter}, + model::{CompiledFunction, EnumDefinition, Output, OutputField}, output::GeneratedFile, render::{Writer, markdown}, targets::{ @@ -221,7 +221,7 @@ fn render_enum(writer: &mut Writer, module: &str, definition: &EnumDefinition, c writer.blank_line(); } -fn render_structure(writer: &mut Writer, name: &str, fields: &[Parameter]) { +fn render_structure(writer: &mut Writer, name: &str, fields: &[OutputField]) { writer.write(format_args!("## `{name}`\n\n")); markdown::code_block(writer, "c", |writer| { writer.line("typedef struct {"); diff --git a/codegen/src/targets/reference/cpp.rs b/codegen/src/targets/reference/cpp.rs index 4b3021f..c329809 100644 --- a/codegen/src/targets/reference/cpp.rs +++ b/codegen/src/targets/reference/cpp.rs @@ -5,7 +5,7 @@ use convert_case::{Case, Casing}; use crate::{ documentation::{self as docs}, - model::{CompiledFunction, EnumDefinition, Output, Parameter}, + model::{CompiledFunction, EnumDefinition, Output, OutputField}, output::GeneratedFile, render::{Writer, markdown}, targets::group_by_source, @@ -193,7 +193,7 @@ fn render_enum(writer: &mut Writer, definition: &EnumDefinition) { writer.blank_line(); } -fn render_structure(writer: &mut Writer, name: &str, fields: &[Parameter]) { +fn render_structure(writer: &mut Writer, name: &str, fields: &[OutputField]) { writer.write(format_args!("## `{name}`\n\n")); markdown::code_block(writer, "cpp", |writer| { writer.line(format_args!("struct {name} {{")); @@ -389,7 +389,7 @@ mod tests { .count(); assert_eq!(umbrella.matches("- [`ptfkit.").count(), source_modules); - assert!(index.find("[`ptfkit`]( ").is_none()); + assert!(!index.contains("[`ptfkit`]( ")); assert!( index.find("[`ptfkit`](modules/ptfkit.md)").unwrap() < index.find("ptfkit.ahuja1984").unwrap() diff --git a/codegen/src/targets/rust.rs b/codegen/src/targets/rust.rs index 292aeba..1789cca 100644 --- a/codegen/src/targets/rust.rs +++ b/codegen/src/targets/rust.rs @@ -57,6 +57,10 @@ pub(crate) fn render(functions: &[CompiledFunction]) -> Result>(); let lookup_conversions = lookup_conversion_tokens(&functions); + let verification_helpers = functions + .iter() + .any(|function| !function.verification_cases.is_empty()) + .then(verification_test_helpers); let mut defined_result_classes = BTreeSet::new(); let definitions = functions .into_iter() @@ -72,7 +76,7 @@ pub(crate) fn render(functions: &[CompiledFunction]) -> Result TokenStream { +fn verification_test_tokens(resolved: &CompiledFunction, unique_test_module: bool) -> TokenStream { let function = format_ident!("{}", resolved.core.name); let module = match unique_test_module { true => format_ident!("{}_tests", resolved.core.name), false => format_ident!("tests"), }; let tests = resolved - .golden_tests + .verification_cases .iter() .map(|case| { let name = format_ident!("{}", case.id); @@ -717,30 +721,76 @@ fn golden_test_tokens(resolved: &CompiledFunction, unique_test_module: bool) -> } }) .collect::>(); - let expected = case - .expected - .iter() - .map(|value| Literal::f64_suffixed(*value)) - .collect::>(); - let atol = Literal::f64_suffixed(case.atol); - let rtol = Literal::f64_suffixed(case.rtol); + let assertion_for = |actual: TokenStream, + expected: &f64, + tolerance: &crate::model::CompiledTolerance| { + let expected = Literal::f64_suffixed(*expected); + let absolute = Literal::f64_suffixed(tolerance.absolute); + let relative = Literal::f64_suffixed(tolerance.relative); + let quantity = Literal::string(&tolerance.quantity); + let unit = Literal::string(&tolerance.unit); + let source = match &tolerance.source { + crate::model::ToleranceSource::Registry => "registry".to_owned(), + crate::model::ToleranceSource::SourceOverride(location) => { + format!("source override: {location}") + } + }; + let source = Literal::string(&source); + quote!(assert_close(#actual, #expected, #absolute, #relative, #quantity, #unit, #source);) + }; let assertions = match &resolved.core.output { - Output::Scalar => { - let expected = &expected[0]; - quote!(assert_close(result, #expected, #atol, #rtol);) - } + Output::Scalar => assertion_for( + quote!(result), + &case.expected[0], + &resolved.output_tolerances[0], + ), Output::Struct(fields) => { - let assertions = fields.iter().zip(&expected).map(|(field, expected)| { - let field = format_ident!("{field}"); - quote!(assert_close(result.#field, #expected, #atol, #rtol);) - }); + let assertions = fields + .iter() + .zip(&case.expected) + .zip(&resolved.output_tolerances) + .map(|((field, expected), tolerance)| { + let field = format_ident!("{field}"); + assertion_for(quote!(result.#field), expected, tolerance) + }); quote!(#(#assertions)*) } }; quote!(#[test] fn #name() { let result = #function(#(#values),*); #assertions }) }) .collect::>(); - quote! { #[cfg(test)] mod #module { use super::*; fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { assert!((actual - expected).abs() <= atol + rtol * expected.abs(), "actual {actual} != expected {expected}"); } #(#tests)* } } + quote! { #[cfg(test)] mod #module { use super::*; #(#tests)* } } +} + +fn verification_test_helpers() -> TokenStream { + let floating_point_guard = Literal::f64_suffixed(crate::compile::FLOATING_POINT_GUARD); + quote! { + #[cfg(test)] + fn resolved_tolerance(expected: f64, absolute: f64, relative: f64) -> f64 { + absolute.max(relative * expected.abs()).max(#floating_point_guard) + } + + #[cfg(test)] + fn assert_close(actual: f64, expected: f64, absolute: f64, relative: f64, quantity: &str, unit: &str, source: &str) { + let difference = (actual - expected).abs(); + let tolerance = resolved_tolerance(expected, absolute, relative); + assert!(difference <= tolerance, "actual={actual}, expected={expected}, difference={difference}, tolerance={tolerance}, quantity={quantity}, unit={unit}, source={source}"); + } + + #[cfg(test)] + mod comparator_tests { + use super::*; + + #[test] + fn accepts_below_and_rejects_above_tolerance() { + for expected in [0.0, 2.0, -2.0] { + let tolerance = resolved_tolerance(expected, 0.001, 0.01); + assert!((expected + tolerance * 0.5 - expected).abs() <= tolerance); + assert!((expected + tolerance * 2.0 - expected).abs() > tolerance); + } + } + } + } } fn render_tokens(module_docs: TokenStream, tokens: TokenStream) -> String { @@ -751,7 +801,7 @@ fn render_tokens(module_docs: TokenStream, tokens: TokenStream) -> String { mod tests { use super::*; use crate::documentation::Returns; - use crate::model::Parameter; + use crate::model::{OutputField, Parameter}; use crate::semantic::{BinaryOp, Expr, MathFunction, Reference}; fn number(value: f64) -> Expr { @@ -932,9 +982,12 @@ mod tests { assert_eq!( return_doc_lines(Returns::Record { name: "Li2007PTFResult", - fields: &[Parameter { + fields: &[OutputField { + reported_unit: "cm^3/cm^3".into(), name: "theta_s".into(), - unit: "cm^3/cm^3".into(), + quantity: "volumetric_water_content".into(), + symbol: None, + unit: "volume_fraction".into(), domain: None, description: "Saturated water content.".into(), }], diff --git a/codegen/src/validate.rs b/codegen/src/validate.rs index cbfb6a0..4d4a496 100644 --- a/codegen/src/validate.rs +++ b/codegen/src/validate.rs @@ -3,10 +3,13 @@ use std::{ path::PathBuf, }; -use crate::model::{Entry, Function, Parameter}; +use crate::model::{Entry, Function, OutputField, QuantityRegistry}; pub(crate) fn specifications(entries: &[Entry]) -> Vec { let mut errors = Vec::new(); + if let Some(entry) = entries.first() { + validate_quantity_registry(&entry.quantities, &mut errors); + } let mut functions = BTreeMap::new(); let mut public = BTreeMap::new(); for entry in entries { @@ -27,6 +30,7 @@ pub(crate) fn specifications(entries: &[Entry]) -> Vec { &mut errors, ); duplicate_input_names(entry, function, &mut errors); + duplicate_verification_case_ids(entry, function, &mut errors); validate_enums(entry, function, &mut errors); duplicate_names( entry, @@ -35,6 +39,8 @@ pub(crate) fn specifications(entries: &[Entry]) -> Vec { "outputs.fields", &mut errors, ); + validate_output_quantities(entry, function, &mut errors); + validate_tolerance_overrides(entry, function, &mut errors); match ( function.outputs.fields().len(), function.result_class().is_some(), @@ -64,6 +70,181 @@ pub(crate) fn specifications(entries: &[Entry]) -> Vec { errors } +fn valid_identifier(id: &str) -> bool { + !id.is_empty() + && id.chars().enumerate().all(|(index, c)| { + c.is_ascii_lowercase() || (index > 0 && (c.is_ascii_digit() || c == '_')) + }) +} + +fn validate_quantity_registry(registry: &QuantityRegistry, errors: &mut Vec) { + for (id, unit) in ®istry.units { + if !valid_identifier(id) { + errors.push(format!("specs/units.yaml: invalid unit identifier `{id}`")); + } + let mut notations = BTreeSet::new(); + for notation in std::iter::once(&unit.preferred_notation).chain(&unit.aliases) { + if notation.trim().is_empty() || !notations.insert(notation) { + errors.push(format!( + "specs/units.yaml: unit `{id}` has an empty or duplicate notation" + )); + } + } + } + for (id, quantity) in ®istry.quantities { + if !valid_identifier(id) { + errors.push(format!( + "specs/quantities.yaml: quantity identifier `{id}` must match ^[a-z][a-z0-9_]*$" + )); + } + if quantity.description.trim().is_empty() { + errors.push(format!( + "specs/quantities.yaml:\n {}:\n description must not be empty", + id + )); + } + if quantity.units.is_empty() { + errors.push(format!( + "specs/quantities.yaml:\n {}:\n at least one unit is required", + id + )); + } + let mut notations = BTreeMap::new(); + for unit_id in quantity.units.keys() { + if let Some(unit) = registry.units.get(unit_id) { + for notation in std::iter::once(&unit.preferred_notation).chain(&unit.aliases) { + if let Some(previous) = notations.insert(notation, unit_id) { + errors.push(format!("specs/quantities.yaml: quantity `{id}` has ambiguous notation `{notation}` for `{previous}` and `{unit_id}`")); + } + } + } + } + for (unit, tolerance) in &quantity.units { + if !registry.units.contains_key(unit) { + errors.push(format!( + "specs/quantities.yaml: quantity `{id}` references unknown unit identifier `{unit}`" + )); + } + validate_tolerance( + "specs/quantities.yaml", + id, + unit, + tolerance.absolute, + tolerance.relative, + errors, + ); + if tolerance.rationale.trim().is_empty() { + errors.push(format!( + "specs/quantities.yaml:\n {} [{}]:\n rationale must not be empty", + id, unit + )); + } + } + } +} + +fn validate_output_quantities(entry: &Entry, function: &Function, errors: &mut Vec) { + for field in function.outputs.fields() { + let Some(quantity) = entry.quantities.quantities.get(&field.quantity) else { + errors.push(diag( + entry, + "outputs", + Some(&function.name), + &format!( + "output `{}` references unknown quantity `{}` with unit `{}`", + field.name, field.quantity, field.unit + ), + )); + continue; + }; + match entry.quantities.units.get(&field.unit) { + None => errors.push(diag( + entry, + "outputs", + Some(&function.name), + &format!( + "output `{}` references unknown unit identifier `{}`", + field.name, field.unit + ), + )), + Some(unit) + if field.reported_unit != unit.preferred_notation + && !unit.aliases.contains(&field.reported_unit) => + { + errors.push(diag(entry, "outputs", Some(&function.name), + &format!("output `{}` reported_unit `{}` is not an equivalent notation for unit `{}`; an explicit registry decision is required; values must not be converted", field.name, field.reported_unit, field.unit))); + } + Some(_) => {} + } + if !quantity.units.contains_key(&field.unit) { + errors.push(diag( + entry, + "outputs", + Some(&function.name), + &format!( + "output `{}` quantity `{}` has no registered unit `{}`", + field.name, field.quantity, field.unit + ), + )); + } + } +} + +fn validate_tolerance_overrides(entry: &Entry, function: &Function, errors: &mut Vec) { + for (name, tolerance) in &function.verification_tolerances { + let Some(field) = function + .outputs + .fields() + .iter() + .find(|field| field.name == *name) + else { + errors.push(diag( + entry, + "verification_tolerances", + Some(&function.name), + &format!("override references unknown output `{name}`"), + )); + continue; + }; + validate_tolerance( + &entry.path.display().to_string(), + &field.quantity, + &field.unit, + tolerance.absolute, + tolerance.relative, + errors, + ); + if tolerance.source_location.trim().is_empty() { + errors.push(diag( + entry, + "verification_tolerances", + Some(&function.name), + &format!("override for output `{name}` requires source_location"), + )); + } + } +} + +fn validate_tolerance( + path: &str, + quantity: &str, + unit: &str, + absolute: f64, + relative: Option, + errors: &mut Vec, +) { + if !absolute.is_finite() || absolute <= 0.0 { + errors.push(format!( + "{path}:\n {quantity} [{unit}]:\n absolute tolerance must be finite and positive" + )); + } + if relative.is_some_and(|value| !value.is_finite() || value < 0.0) { + errors.push(format!( + "{path}:\n {quantity} [{unit}]:\n relative tolerance must be finite and non-negative" + )); + } +} + fn duplicate_input_names(entry: &Entry, function: &Function, errors: &mut Vec) { let mut seen = BTreeSet::new(); for value in &function.inputs { @@ -78,6 +259,20 @@ fn duplicate_input_names(entry: &Entry, function: &Function, errors: &mut Vec) { + let mut seen = BTreeSet::new(); + for case in &function.verification_cases { + if !seen.insert(&case.id) { + errors.push(diag( + entry, + "verification_cases", + Some(&function.name), + &format!("duplicate id `{}`", case.id), + )); + } + } +} + fn validate_enums(entry: &Entry, function: &Function, errors: &mut Vec) { let mut validated = BTreeSet::new(); for input in &function.inputs { @@ -146,7 +341,7 @@ fn duplicate( fn duplicate_names( entry: &Entry, function: &Function, - values: &[Parameter], + values: &[OutputField], field: &str, errors: &mut Vec, ) { diff --git a/docs/src/contributing/development.md b/docs/src/contributing/development.md index 8a8382c..a9bc991 100644 --- a/docs/src/contributing/development.md +++ b/docs/src/contributing/development.md @@ -74,6 +74,9 @@ lock entry. The [PTF source specification guide](../ptf-catalog/index.md) explains the scientific information represented by each YAML file. The JSON Schema and `mise run validate` define the complete structural and semantic contract. +Verification-case provenance and the shared cross-target comparison policy are +documented in the [scientific verification policy](verification.md). Calculated +expected values are fixed in YAML and are not regenerated from semantic IR. Do not edit generated target sources, tests, the PTF catalog, or generated API reference pages directly. Regenerate every target and the PTF catalog with: @@ -144,7 +147,7 @@ mise run corpus-report --format json All counts are derived from `specs/functions/` through the normal loader, validation, and compilation pipeline. The report intentionally includes schema-valid `blocked` functions. Its verification coverage describes declared -golden tests and edge cases in the specifications; it is neither predictive +verification cases and edge cases in the specifications; it is neither predictive benchmarking nor external validation against soil datasets, and descriptive edge cases are not claimed to be executable tests. diff --git a/docs/src/contributing/verification.md b/docs/src/contributing/verification.md new file mode 100644 index 0000000..050ae98 --- /dev/null +++ b/docs/src/contributing/verification.md @@ -0,0 +1,131 @@ +--- +title: Scientific verification policy +--- + +# Scientific verification policy + +Verification cases fix representative inputs and expected outputs in each PTF +specification. They test generated implementations; they do not replace model +evaluation against observations. + +## Provenance + +Only two provenance kinds are supported: + +- `published` means the complete input-output pair appears in the publication, + supplementary material, official author software, or another authoritative + source. `source_location` records where it can be checked. +- `calculated` means no complete numerical example was published. A reviewer + selected a physically meaningful input and calculated the expected output + from the published computational contract. `rationale` explains that choice. + `source_location` may additionally identify a published input row or other + provenance used to select the calculated case. + +Both kinds require `inputs` and `expected`. Record-valued functions require +every output field. Case IDs must be unique within each function and valid as +Rust test function names. Use descriptive lowercase `snake_case` identifiers and +avoid Rust keywords. Specification validation checks uniqueness but does not +validate Rust identifier syntax. Invalid names can cause Rust code generation +or subsequent compilation to fail. + +A calculated case is implementation verification, not independent validation +of the PTF's scientific accuracy and not evidence of parity with an unpublished +artifact. + +```yaml +verification_cases: + - id: representative_soil + kind: calculated + inputs: + clay: 30.0 + silt: 20.0 + soil_organic_carbon: 1.0 + expected: + field_capacity: 0.213 + permanent_wilting_point: 0.117 + rationale: >- + Interior physically plausible mineral-soil composition using mass + percentages for texture and soil organic carbon. +``` + +A published lookup row instead records its source: + +```yaml +verification_cases: + - id: published_table_case + kind: published + inputs: {soil_texture: sand} + expected: {coefficient: 1.25} + source_location: "Table 4, row 2" + notes: Values transcribed from the primary source. +``` + +The absence of a published example, row-level dataset, supplement, or author +calculator does not block extraction or implementation when the formula, +coefficients, units, preprocessing, transforms, branches, applicability, and +other parts of the computational contract are complete. Missing or ambiguous +parts of that contract remain blockers. Rights and licensing are a separate +release gate. + +## Numerical comparison + +Every scalar output and record field declares a stable physical or model +`quantity`. The repository-level `specs/quantities.yaml` registry assigns a +reviewed project-default tolerance to each normalized quantity-unit combination. +`specs/units.yaml` owns equivalent notations only; output `reported_unit` preserves +the source spelling. Aliases never convert numerical values, and tolerances remain +specific to the quantity × unit pair. +An unknown quantity or unit is a validation error; there is no global +scientific fallback and no implicit unit conversion. + +A publication may support a more appropriate function-output tolerance. Such +an override is declared once on the function, not in an individual case: + +```yaml +verification_tolerances: + field_capacity: + absolute: 0.005 + relative: 0.01 + source_location: "Table 5" +``` + +The cited override replaces the registry default. Model-performance metrics +such as RMSE, MAE, bias, or R² must not be converted mechanically into +implementation tolerances. + +All Rust, Python, C, and C++ generated tests resolve the same comparison: + +```text +scientific_tolerance = max(absolute, relative * abs(expected)) +abs(actual - expected) <= max(scientific_tolerance, floating_point_guard) +``` + +`absolute` is mandatory and expressed in the output unit; `relative` is +optional and dimensionless. The centrally generated floating-point guard only +absorbs insignificant implementation and math-library variation and is much +smaller than normal scientific tolerances. Failure messages report actual and +expected values, their difference, the resolved tolerance, quantity, unit, and +whether the policy came from the registry or a cited source override. +Specification compilation rejects any case whose resolved scientific tolerance +is greater than or equal to the magnitude of a non-zero expected value, because +such a comparison would accept zero. + +Registry values are transparent ptfkit verification policy. They are not +universal metrological claims, measurement uncertainty, or evidence of PTF +predictive accuracy. Stored expected values may retain extra digits for stable +regeneration without implying that every digit is scientifically significant. + +## Selecting calculated cases + +Prefer a complete published predictor row without an output, then published +means or medians, then an interior point of the documented domain, and finally +an expert-selected typical soil. Inputs must use the stated units and scales, +stay away from boundaries, keep texture components and logarithm arguments +valid, and respect physical relations such as +`theta_1500 <= theta_33 <= theta_s`. + +Piecewise functions, decision trees, and other multi-branch models need at +least one case for every material computational branch. Use published cases +where they exist and calculated cases for the remaining branches. Expected +calculated outputs are produced once with simple reference code and retained in +YAML; generation does not recalculate them. diff --git a/docs/src/ptf-catalog/index.md b/docs/src/ptf-catalog/index.md index c74e3ee..a04450c 100644 --- a/docs/src/ptf-catalog/index.md +++ b/docs/src/ptf-catalog/index.md @@ -65,9 +65,11 @@ should check both the function status and the API reference for their target. ## Inputs, quantities, units, and domains -Quantitative inputs and outputs identify a scientific quantity through a name, -symbol, unit, domain, and description. These values must preserve the source's -definitions and conversions. A domain records the published calibration or +Quantitative inputs identify a source variable through a name, symbol, unit, +domain, and description. Outputs additionally carry a stable `quantity` +identifier resolved against `specs/quantities.yaml`, a normalized `unit` identifier +from `specs/units.yaml`, and `reported_unit` preserving the literal source notation. +These values must preserve the source's definitions and numerical values. A domain records the published calibration or mathematical range; it does not imply that every target performs runtime range validation. @@ -83,6 +85,30 @@ are stable public type names, while field order is part of the cross-target result contract. Reusable parameter declarations, enum types, and record shapes may be declared once in `$defs` and referenced by multiple functions. The `$defs` key is the canonical name of a reusable declaration, type, or record. +Both registries are top-level maps keyed by stable identifiers. The unit registry +owns only `preferred_notation` and equivalent `aliases`. The quantity registry +lists allowed unit identifiers and owns each quantity × unit tolerance. + +```yaml +quantity: volumetric_water_content +unit: volume_percent +reported_unit: "vol.%" +``` + +Alias matching is exact and contextual: the reported notation must equal the +selected unit's preferred notation or an alias, and the quantity must permit that +unit. Ambiguous notations such as `%`, `1`, and `dimensionless` are interpreted +only within the quantity's allowed units. Multiple units within one quantity +must not claim the same notation. + +Aliases preserve numerical values exactly. `vol.%`, `% v/v`, and +`% volume/volume` share a representation; `cm³/cm³` and `cm^3/cm^3` do too. +Volume fractions and volume percentages, `mm/h` and `cm/h`, and `kPa` and +`cm H2O` remain distinct. Normalization never multiplies, divides, offsets, or +otherwise transforms a value. Missing identifiers, unsupported quantity/unit +pairs, and unmatched source notations block validation and require an explicit +registry decision. Registry edits must be reviewed for numerical equivalence; +validation checks the declared aliases, not physical conversion formulas. ## Scientific evidence and numerical expectations @@ -91,8 +117,10 @@ Specifications retain more than executable formulas: - `scientific_notes` records derivations, source notation, numerical policy, and review decisions that apply to the source; - documentation notes and warnings communicate function-specific limitations; -- `golden_tests` preserve representative input and expected-output cases with - explicit tolerances and provenance notes; +- `verification_cases` preserve representative inputs and fixed expected + outputs with `published` or `calculated` provenance; comparison is defined by + the [verification policy](../contributing/verification.md), with reviewed + function-output overrides recorded in YAML when the source supports them; - `edge_cases` record boundary conditions and the expected behavior. This information must be supported by the publication or by an explicit, @@ -103,7 +131,14 @@ can prevent a function from advancing beyond `draft` or `blocked`. ## Implementation data Functions marked `ready-for-implementation` or `implemented` include an -`implementation`. Implementations express ordered variables used to reproduce +`implementation` and at least one verification case. Verification-case IDs are +required to be unique within each function and valid as Rust test function +names; descriptive lowercase `snake_case` is recommended, avoiding Rust keywords. +Specification validation checks uniqueness, not Rust identifier syntax; invalid +names may fail during Rust code generation or compilation. See the +[verification policy](../contributing/verification.md) for details. + +Implementations express ordered variables used to reproduce the published PTF. A variable can be populated by a formula or by a typed lookup. Enums, records, and lookups are independent reusable definitions: a lookup maps an enum member to a record, and later formulas can access fields of that record. diff --git a/specs/functions/ahuja1984.yaml b/specs/functions/ahuja1984.yaml index 3527a28..c3bfdc7 100644 --- a/specs/functions/ahuja1984.yaml +++ b/specs/functions/ahuja1984.yaml @@ -59,12 +59,15 @@ functions: outputs: type: scalar name: k_sat + quantity: saturated_hydraulic_conductivity symbol: Ks - unit: cm/h + unit: centimeter_per_hour + reported_unit: cm/h domain: value >= 0 description: Saturated hydraulic conductivity. - golden_tests: + verification_cases: - id: exponent_four + kind: calculated inputs: total_porosity: 0.45 theta_33: 0.25 @@ -72,10 +75,9 @@ functions: exponent_n: 4.0 expected: k_sat: 0.16 - rtol: 1.0e-12 - atol: 1.0e-14 - notes: Algebraic verification of equation 3 with the paper's typical exponent n = 4. + rationale: Algebraic verification of equation 3 with the paper's typical exponent n = 4. - id: exponent_five + kind: calculated inputs: total_porosity: 0.5 theta_33: 0.2 @@ -83,9 +85,7 @@ functions: exponent_n: 5.0 expected: k_sat: 0.243 - rtol: 1.0e-12 - atol: 1.0e-14 - notes: Algebraic verification of equation 3 with the evaluated exponent n = 5. + rationale: Algebraic verification of equation 3 with the evaluated exponent n = 5. edge_cases: - id: zero_effective_porosity inputs: @@ -155,7 +155,7 @@ scientific_notes: | `B` and `n` are explicit inputs because the paper does not provide transferable values for either empirical constant. This API implements equation 3 without - presenting a soil-specific fit as universal. The golden cases are algebraic + presenting a soil-specific fit as universal. The verification cases are algebraic checks of equation 3, not observed values from the experimental datasets. The sample-reduction scaling equations remain outside this scalar API. Their diff --git a/specs/functions/aimrun2009.yaml b/specs/functions/aimrun2009.yaml index c67637e..240e532 100644 --- a/specs/functions/aimrun2009.yaml +++ b/specs/functions/aimrun2009.yaml @@ -47,12 +47,15 @@ functions: outputs: type: scalar name: k_sat + quantity: saturated_hydraulic_conductivity symbol: Ks - unit: m/s + unit: meter_per_second + reported_unit: m/s domain: value >= 0 description: Saturated hydraulic conductivity. - golden_tests: + verification_cases: - id: mean_topsoil_layer + kind: calculated inputs: clay: 43.88 bulk_density: 0.94 @@ -60,10 +63,9 @@ functions: gmd: 0.01 expected: k_sat: 7.358406556179513e-08 - rtol: 1.0e-08 - atol: 1.0e-12 - notes: Calculated from equation (10) and unit conversion. + rationale: Calculated from equation (10) and unit conversion. - id: mean_hardpan_layer + kind: calculated inputs: clay: 50.21 bulk_density: 1.19 @@ -71,10 +73,9 @@ functions: gmd: 0.007 expected: k_sat: 3.07872446717209e-08 - rtol: 1.0e-08 - atol: 1.0e-12 - notes: Calculated from equation (10) and unit conversion. + rationale: Calculated from equation (10) and unit conversion. - id: mean_subsoil_layer + kind: calculated inputs: clay: 58.81 bulk_density: 1.13 @@ -82,10 +83,9 @@ functions: gmd: 0.005 expected: k_sat: 2.3343051908963327e-08 - rtol: 1.0e-08 - atol: 1.0e-12 - notes: Calculated from equation (10) and unit conversion. + rationale: Calculated from equation (10) and unit conversion. - id: min_organic_matter + kind: calculated inputs: clay: 47.5 bulk_density: 1.08 @@ -93,9 +93,7 @@ functions: gmd: 0.008 expected: k_sat: 3.831168764444974e-08 - rtol: 1.0e-08 - atol: 1.0e-12 - notes: Calculated from equation (10) and unit conversion. + rationale: Calculated from equation (10) and unit conversion. edge_cases: - id: non_positive_log_input inputs: diff --git a/specs/functions/beniaich2023.yaml b/specs/functions/beniaich2023.yaml index 8e6dc3f..adc2a05 100644 --- a/specs/functions/beniaich2023.yaml +++ b/specs/functions/beniaich2023.yaml @@ -48,18 +48,24 @@ $defs: name: Beniaich2023PTFResult fields: - name: water_saturation + quantity: gravimetric_water_content symbol: w_0 - unit: g/g + unit: mass_fraction + reported_unit: g/g domain: null description: Gravimetric water content at saturation. - name: water_field_capacity + quantity: gravimetric_water_content symbol: w_330 - unit: g/g + unit: mass_fraction + reported_unit: g/g domain: null description: Gravimetric water content at -33 kPa. - name: water_wilting_point + quantity: gravimetric_water_content symbol: w_15000 - unit: g/g + unit: mass_fraction + reported_unit: g/g domain: null description: Gravimetric water content at -1,500 kPa. functions: @@ -86,17 +92,16 @@ functions: expr: (10.277 + 0.365 * clay) / 100 - name: water_wilting_point expr: (3.081 + 0.327 * clay) / 100 - golden_tests: + verification_cases: - id: representative_case + kind: calculated inputs: clay: 20.0 expected: water_saturation: 0.57427 water_field_capacity: 0.17577 water_wilting_point: 0.09621 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated from Table 5 and divided by 100. + rationale: Calculated from Table 5 and divided by 100. edge_cases: [] documentation: notes: @@ -128,17 +133,16 @@ functions: expr: (16.178 + 0.290 * silt) / 100 - name: water_wilting_point expr: (10.521 + 0.187 * silt) / 100 - golden_tests: + verification_cases: - id: representative_case + kind: calculated inputs: silt: 30.0 expected: water_saturation: 0.68478 water_field_capacity: 0.24878 water_wilting_point: 0.16131 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated from Table 5 and divided by 100. + rationale: Calculated from Table 5 and divided by 100. edge_cases: [] documentation: notes: @@ -170,17 +174,16 @@ functions: expr: (34.680 - 0.324 * sand) / 100 - name: water_wilting_point expr: (23.927 - 0.257 * sand) / 100 - golden_tests: + verification_cases: - id: representative_case + kind: calculated inputs: sand: 50.0 expected: water_saturation: 0.6007 water_field_capacity: 0.1848 water_wilting_point: 0.11077 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated from Table 5 and divided by 100. + rationale: Calculated from Table 5 and divided by 100. edge_cases: [] documentation: notes: @@ -215,8 +218,9 @@ functions: expr: (45.178 - 0.290 * clay_silt) / 100 - name: water_wilting_point expr: (29.265 - 0.187 * clay_silt) / 100 - golden_tests: + verification_cases: - id: representative_case + kind: calculated inputs: clay: 20.0 silt: 30.0 @@ -224,9 +228,7 @@ functions: water_saturation: 0.74501 water_field_capacity: 0.30678 water_wilting_point: 0.19915 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated from Table 5 and divided by 100. + rationale: Calculated from Table 5 and divided by 100. edge_cases: [] documentation: notes: @@ -261,8 +263,9 @@ functions: expr: (23.278 + 1.819 * clay_silt_ratio) / 100 - name: water_wilting_point expr: (16.298 - 0.244 * clay_silt_ratio) / 100 - golden_tests: + verification_cases: - id: representative_case + kind: calculated inputs: clay: 20.0 silt: 40.0 @@ -270,9 +273,7 @@ functions: water_saturation: 0.68578 water_field_capacity: 0.241875 water_wilting_point: 0.16176 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated from Table 5 and divided by 100. + rationale: Calculated from Table 5 and divided by 100. edge_cases: - id: zero_silt inputs: @@ -310,17 +311,16 @@ functions: expr: (21.331 + 1.339 * soil_organic_matter) / 100 - name: water_wilting_point expr: (13.758 + 0.902 * soil_organic_matter) / 100 - golden_tests: + verification_cases: - id: representative_case + kind: calculated inputs: soil_organic_matter: 2.0 expected: water_saturation: 0.66749 water_field_capacity: 0.24009 water_wilting_point: 0.15562 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated from Table 5 and divided by 100. + rationale: Calculated from Table 5 and divided by 100. edge_cases: [] documentation: notes: @@ -355,8 +355,9 @@ functions: expr: (35.844 - 0.085 * silt - 0.359 * sand + 0.947 * soil_organic_matter) / 100 - name: water_wilting_point expr: (28.734 - 0.148 * silt - 0.324 * sand + 0.636 * soil_organic_matter) / 100 - golden_tests: + verification_cases: - id: representative_case + kind: calculated inputs: silt: 30.0 sand: 50.0 @@ -365,9 +366,7 @@ functions: water_saturation: 0.56266 water_field_capacity: 0.17238 water_wilting_point: 0.09366 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated from Table 6 and divided by 100. + rationale: Calculated from Table 6 and divided by 100. edge_cases: [] documentation: notes: @@ -400,8 +399,9 @@ functions: expr: (32.227 - 0.320 * sand + 0.899 * soil_organic_matter) / 100 - name: water_wilting_point expr: (22.421 - 0.254 * sand + 0.552 * soil_organic_matter) / 100 - golden_tests: + verification_cases: - id: representative_case + kind: calculated inputs: sand: 50.0 soil_organic_matter: 2.0 @@ -409,9 +409,7 @@ functions: water_saturation: 0.58954 water_field_capacity: 0.18025 water_wilting_point: 0.10825 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated from Table 6 and divided by 100. + rationale: Calculated from Table 6 and divided by 100. edge_cases: [] documentation: notes: @@ -444,8 +442,9 @@ functions: expr: (13.847 + 0.281 * silt + 0.999 * soil_organic_matter) / 100 - name: water_wilting_point expr: (8.929 + 0.182 * silt + 0.683 * soil_organic_matter) / 100 - golden_tests: + verification_cases: - id: representative_case + kind: calculated inputs: silt: 30.0 soil_organic_matter: 2.0 @@ -453,9 +452,7 @@ functions: water_saturation: 0.67031 water_field_capacity: 0.24275 water_wilting_point: 0.15755 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated from Table 6 and divided by 100. + rationale: Calculated from Table 6 and divided by 100. edge_cases: [] documentation: notes: @@ -488,8 +485,9 @@ functions: expr: (7.023 + 0.364 * clay + 1.278 * soil_organic_matter) / 100 - name: water_wilting_point expr: (0.923 + 0.327 * clay + 0.847 * soil_organic_matter) / 100 - golden_tests: + verification_cases: - id: representative_case + kind: calculated inputs: clay: 20.0 soil_organic_matter: 2.0 @@ -497,9 +495,7 @@ functions: water_saturation: 0.5589 water_field_capacity: 0.16859 water_wilting_point: 0.09157 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated from Table 6 and divided by 100. + rationale: Calculated from Table 6 and divided by 100. edge_cases: [] documentation: notes: @@ -534,8 +530,9 @@ functions: expr: (-0.094 + 0.359 * clay + 0.274 * silt + 0.947 * soil_organic_matter) / 100 - name: water_wilting_point expr: (-3.623 + 0.324 * clay + 0.175 * silt + 0.636 * soil_organic_matter) / 100 - golden_tests: + verification_cases: - id: representative_case + kind: calculated inputs: clay: 20.0 silt: 30.0 @@ -544,9 +541,7 @@ functions: water_saturation: 0.56229 water_field_capacity: 0.172 water_wilting_point: 0.09379 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated from Table 6 and divided by 100. + rationale: Calculated from Table 6 and divided by 100. edge_cases: [] documentation: notes: @@ -574,7 +569,7 @@ functions: - $ref: "#/$defs/soil_organic_matter" outputs: $ref: "#/$defs/Beniaich2023PTFResult" - golden_tests: [] + verification_cases: [] edge_cases: [] documentation: notes: @@ -599,7 +594,7 @@ functions: - $ref: "#/$defs/soil_organic_matter" outputs: $ref: "#/$defs/Beniaich2023PTFResult" - golden_tests: [] + verification_cases: [] edge_cases: [] documentation: notes: @@ -626,7 +621,7 @@ functions: - $ref: "#/$defs/soil_organic_matter" outputs: $ref: "#/$defs/Beniaich2023PTFResult" - golden_tests: [] + verification_cases: [] edge_cases: [] documentation: notes: diff --git a/specs/functions/chakraborty2011.yaml b/specs/functions/chakraborty2011.yaml index 7bb486c..6865496 100644 --- a/specs/functions/chakraborty2011.yaml +++ b/specs/functions/chakraborty2011.yaml @@ -48,23 +48,31 @@ $defs: name: Chakraborty2011PTFResult fields: - name: water_content_33 + quantity: gravimetric_water_content symbol: w_33 - unit: g/g + unit: mass_fraction + reported_unit: g/g domain: null description: Gravimetric water content at -33 kPa matric potential. - name: water_content_100 + quantity: gravimetric_water_content symbol: w_100 - unit: g/g + unit: mass_fraction + reported_unit: g/g domain: null description: Gravimetric water content at -100 kPa matric potential. - name: water_content_500 + quantity: gravimetric_water_content symbol: w_500 - unit: g/g + unit: mass_fraction + reported_unit: g/g domain: null description: Gravimetric water content at -500 kPa matric potential. - name: water_content_1500 + quantity: gravimetric_water_content symbol: w_1500 - unit: g/g + unit: mass_fraction + reported_unit: g/g domain: null description: Gravimetric water content at -1500 kPa matric potential. functions: @@ -95,8 +103,9 @@ functions: expr: (0.251 * clay + 0.185 * silt + 2.958) / 100 - name: water_content_1500 expr: (0.184 * clay + 0.144 * silt + 3.702) / 100 - golden_tests: + verification_cases: - id: representative_case + kind: calculated inputs: clay: 20.0 silt: 30.0 @@ -105,9 +114,7 @@ functions: water_content_100: 0.19758 water_content_500: 0.13528 water_content_1500: 0.11702 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated directly from Table 3 and divided by 100. + rationale: Calculated directly from Table 3 and divided by 100. edge_cases: [] documentation: notes: @@ -146,8 +153,9 @@ functions: expr: (-0.244 * sand - 0.795 * bulk_density + 27.495) / 100 - name: water_content_1500 expr: (-0.187 * sand + 1.241 * bulk_density + 19.320) / 100 - golden_tests: + verification_cases: - id: representative_case + kind: calculated inputs: sand: 50.0 bulk_density: 1.5 @@ -156,9 +164,7 @@ functions: water_content_100: 0.175765 water_content_500: 0.141025 water_content_1500: 0.118315 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated directly from Table 3 and divided by 100. + rationale: Calculated directly from Table 3 and divided by 100. edge_cases: [] documentation: notes: @@ -198,8 +204,9 @@ functions: expr: (0.244 * clay + 0.187 * silt - 3.079 * bulk_density + 8.046) / 100 - name: water_content_1500 expr: (0.183 * clay + 0.144 * silt - 0.689 * bulk_density + 4.840) / 100 - golden_tests: + verification_cases: - id: representative_case + kind: calculated inputs: clay: 20.0 silt: 30.0 @@ -209,9 +216,7 @@ functions: water_content_100: 0.201085 water_content_500: 0.139175 water_content_1500: 0.117865 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated directly from Table 3 and divided by 100. + rationale: Calculated directly from Table 3 and divided by 100. edge_cases: [] documentation: notes: @@ -251,8 +256,9 @@ functions: expr: (0.067 * clay - 0.008 * silt - 0.204 * sand + 22.216) / 100 - name: water_content_1500 expr: (0.021 * clay - 0.028 * silt - 0.179 * sand + 20.695) / 100 - golden_tests: + verification_cases: - id: representative_case + kind: calculated inputs: clay: 20.0 silt: 30.0 @@ -262,9 +268,7 @@ functions: water_content_100: 0.19235 water_content_500: 0.13116 water_content_1500: 0.11325 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated directly from Table 3 and divided by 100. + rationale: Calculated directly from Table 3 and divided by 100. edge_cases: [] documentation: notes: @@ -310,8 +314,9 @@ functions: - name: water_content_1500 expr: >- (0.014 * clay - 0.041 * silt - 0.192 * sand + 2.093 * bulk_density + 18.470) / 100 - golden_tests: + verification_cases: - id: representative_case + kind: calculated inputs: clay: 20.0 silt: 30.0 @@ -322,9 +327,7 @@ functions: water_content_100: 0.19166 water_content_500: 0.131065 water_content_1500: 0.110595 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated directly from Table 3 and divided by 100. + rationale: Calculated directly from Table 3 and divided by 100. edge_cases: [] documentation: notes: @@ -372,8 +375,9 @@ functions: - name: water_content_1500 expr: >- (0.017 * clay - 0.053 * silt - 0.184 * sand + 2.950 * organic_carbon + 2.327 * bulk_density + 16.802) / 100 - golden_tests: + verification_cases: - id: representative_case + kind: calculated inputs: clay: 20.0 silt: 30.0 @@ -385,9 +389,7 @@ functions: water_content_100: 0.19695 water_content_500: 0.13364 water_content_1500: 0.113175 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated directly from Table 3 and divided by 100. + rationale: Calculated directly from Table 3 and divided by 100. edge_cases: [] documentation: notes: @@ -424,7 +426,7 @@ scientific_notes: | ## Numeric policy Use `f64`, do not round intermediate or final values, and propagate IEEE 754 - NaN and infinities. Golden cases are direct evaluations of the printed Table 3 + NaN and infinities. Verification cases are direct evaluations of the printed Table 3 coefficients at representative inputs, with source percentage predictions divided by 100 to obtain g/g. diff --git a/specs/functions/clapp1978.yaml b/specs/functions/clapp1978.yaml index 19d4745..0b68d3f 100644 --- a/specs/functions/clapp1978.yaml +++ b/specs/functions/clapp1978.yaml @@ -53,35 +53,47 @@ $defs: name: Clapp1978Parameters fields: - name: b + quantity: campbell_b symbol: b - unit: "1" + unit: dimensionless + reported_unit: "1" domain: value > 0 description: Mean exponent of the moisture-characteristic power curve. - name: saturation_suction + quantity: matric_potential symbol: psi_s(log) - unit: cm + unit: centimeter_water_head + reported_unit: cm domain: value > 0 description: >- Representative saturation suction, calculated as the antilog of the mean logarithm of fitted saturation-suction values. - name: wetting_front_suction + quantity: matric_potential symbol: psi_f - unit: cm + unit: centimeter_water_head + reported_unit: cm domain: value > 0 description: Representative Green-Ampt wetting-front suction. - name: saturated_water_content + quantity: volumetric_water_content symbol: theta_s - unit: "1" + unit: volume_fraction + reported_unit: "1" domain: 0 < value < 1 description: Mean saturated volumetric water content, taken as total porosity. - name: saturated_hydraulic_conductivity + quantity: saturated_hydraulic_conductivity symbol: K_s - unit: cm/min + unit: centimeter_per_minute + reported_unit: cm/min domain: value > 0 description: Mean saturated hydraulic conductivity reported by Li et al. (1976). - name: sorptivity + quantity: sorptivity symbol: S - unit: cm/min^(1/2) + unit: centimeter_per_square_root_minute + reported_unit: cm/min^(1/2) domain: value > 0 description: >- Representative sorptivity for the paper's single initial moisture @@ -138,72 +150,72 @@ functions: description: USDA soil textural class used to select a row of Table 2. outputs: $ref: "#/$defs/Clapp1978Parameters" - golden_tests: + verification_cases: - id: table_2_sand + kind: published + source_location: "Table 2" inputs: {soil_texture: sand} expected: {b: 4.05, saturation_suction: 3.50, wetting_front_suction: 4.66, saturated_water_content: 0.395, saturated_hydraulic_conductivity: 1.056, sorptivity: 1.52} - rtol: 0.0 - atol: 0.0 notes: Direct representative values for Sand from Table 2. - id: table_2_loamy_sand + kind: published + source_location: "Table 2" inputs: {soil_texture: loamy_sand} expected: {b: 4.38, saturation_suction: 1.78, wetting_front_suction: 2.38, saturated_water_content: 0.410, saturated_hydraulic_conductivity: 0.938, sorptivity: 1.04} - rtol: 0.0 - atol: 0.0 notes: Direct representative values for Loamy sand from Table 2. - id: table_2_sandy_loam + kind: published + source_location: "Table 2" inputs: {soil_texture: sandy_loam} expected: {b: 4.90, saturation_suction: 7.18, wetting_front_suction: 9.52, saturated_water_content: 0.435, saturated_hydraulic_conductivity: 0.208, sorptivity: 1.03} - rtol: 0.0 - atol: 0.0 notes: Direct representative values for Sandy loam from Table 2. - id: table_2_silt_loam + kind: published + source_location: "Table 2" inputs: {soil_texture: silt_loam} expected: {b: 5.30, saturation_suction: 56.6, wetting_front_suction: 75.3, saturated_water_content: 0.485, saturated_hydraulic_conductivity: 0.0432, sorptivity: 1.26} - rtol: 0.0 - atol: 0.0 notes: Direct representative values for Silt loam from Table 2. - id: table_2_loam + kind: published + source_location: "Table 2" inputs: {soil_texture: loam} expected: {b: 5.39, saturation_suction: 14.6, wetting_front_suction: 20.0, saturated_water_content: 0.451, saturated_hydraulic_conductivity: 0.0417, sorptivity: 0.693} - rtol: 0.0 - atol: 0.0 notes: Direct representative values for Loam from Table 2. - id: table_2_sandy_clay_loam + kind: published + source_location: "Table 2" inputs: {soil_texture: sandy_clay_loam} expected: {b: 7.12, saturation_suction: 8.63, wetting_front_suction: 11.7, saturated_water_content: 0.420, saturated_hydraulic_conductivity: 0.0378, sorptivity: 0.488} - rtol: 0.0 - atol: 0.0 notes: Direct representative values for Sandy clay loam from Table 2. - id: table_2_silty_clay_loam + kind: published + source_location: "Table 2" inputs: {soil_texture: silty_clay_loam} expected: {b: 7.75, saturation_suction: 14.6, wetting_front_suction: 19.7, saturated_water_content: 0.477, saturated_hydraulic_conductivity: 0.0102, sorptivity: 0.310} - rtol: 0.0 - atol: 0.0 notes: Direct representative values for Silty clay loam from Table 2. - id: table_2_clay_loam + kind: published + source_location: "Table 2" inputs: {soil_texture: clay_loam} expected: {b: 8.52, saturation_suction: 36.1, wetting_front_suction: 48.1, saturated_water_content: 0.476, saturated_hydraulic_conductivity: 0.0147, sorptivity: 0.537} - rtol: 0.0 - atol: 0.0 notes: Direct representative values for Clay loam from Table 2. - id: table_2_sandy_clay + kind: published + source_location: "Table 2" inputs: {soil_texture: sandy_clay} expected: {b: 10.4, saturation_suction: 6.16, wetting_front_suction: 8.18, saturated_water_content: 0.426, saturated_hydraulic_conductivity: 0.0130, sorptivity: 0.223} - rtol: 0.0 - atol: 0.0 notes: Direct representative values for Sandy clay from Table 2. - id: table_2_silty_clay + kind: published + source_location: "Table 2" inputs: {soil_texture: silty_clay} expected: {b: 10.4, saturation_suction: 17.4, wetting_front_suction: 23.0, saturated_water_content: 0.492, saturated_hydraulic_conductivity: 0.0062, sorptivity: 0.242} - rtol: 0.0 - atol: 0.0 notes: Direct representative values for Silty clay from Table 2. - id: table_2_clay + kind: published + source_location: "Table 2" inputs: {soil_texture: clay} expected: {b: 11.4, saturation_suction: 18.6, wetting_front_suction: 24.3, saturated_water_content: 0.482, saturated_hydraulic_conductivity: 0.0077, sorptivity: 0.268} - rtol: 0.0 - atol: 0.0 notes: Direct representative values for Clay from Table 2. edge_cases: [] documentation: diff --git a/specs/functions/cosby1984.yaml b/specs/functions/cosby1984.yaml index 6475c6a..c609ae9 100644 --- a/specs/functions/cosby1984.yaml +++ b/specs/functions/cosby1984.yaml @@ -42,44 +42,59 @@ functions: name: Cosby1984UnivariatePTFResult fields: - name: mean_b + quantity: campbell_b symbol: b unit: dimensionless + reported_unit: dimensionless domain: null description: Mean slope of the moisture characteristic. - name: mean_log_psi_s + quantity: log_saturation_potential symbol: log_psi_s - unit: reported log value + unit: reported_log_value + reported_unit: reported log value domain: null description: Mean log saturation matric potential; the underlying potential is expressed in cm H2O. - name: mean_log_k_sat + quantity: log_saturated_hydraulic_conductivity symbol: log_k_sat - unit: reported log value + unit: reported_log_value + reported_unit: reported log value domain: null description: Mean log saturated hydraulic conductivity; the underlying conductivity is expressed in inches per hour. - name: mean_theta_s + quantity: volumetric_water_content symbol: theta_s - unit: '% volume/volume' + unit: volume_percent + reported_unit: '% volume/volume' domain: null description: Mean saturated water content. - name: sd_b + quantity: campbell_b symbol: b unit: dimensionless + reported_unit: dimensionless domain: null description: Standard deviation of b. - name: sd_log_k_sat + quantity: log_saturated_hydraulic_conductivity symbol: log_k_sat - unit: reported log value + unit: reported_log_value + reported_unit: reported log value domain: null description: Standard deviation of log saturated hydraulic conductivity. - name: sd_theta_s + quantity: volumetric_water_content symbol: theta_s - unit: '% volume/volume' + unit: volume_percent + reported_unit: '% volume/volume' domain: null description: Standard deviation of saturated water content. - golden_tests: + verification_cases: - id: scalar_mid_texture + kind: calculated inputs: sand: 50.0 silt: 30.0 @@ -92,10 +107,9 @@ functions: sd_b: 2.34 sd_log_k_sat: 0.5553 sd_theta_s: 6.27 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated directly from Table 5 formulas for pilot testing. + rationale: Calculated directly from Table 5 formulas for pilot testing. - id: scalar_sandy_texture + kind: calculated inputs: sand: 80.0 silt: 15.0 @@ -108,9 +122,7 @@ functions: sd_b: 1.59 sd_log_k_sat: 0.50715 sd_theta_s: 7.365 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated directly from Table 5 formulas for pilot testing. + rationale: Calculated directly from Table 5 formulas for pilot testing. edge_cases: - id: nan_input inputs: diff --git a/specs/functions/dharumarajan2019.yaml b/specs/functions/dharumarajan2019.yaml index b280b71..9149b65 100644 --- a/specs/functions/dharumarajan2019.yaml +++ b/specs/functions/dharumarajan2019.yaml @@ -75,13 +75,17 @@ $defs: name: Dharumarajan2019WaterRetentionResult fields: - name: field_capacity + quantity: gravimetric_water_content symbol: FC - unit: "%" + unit: mass_percent + reported_unit: "%" domain: null description: Soil water content at -33 kPa matric potential. - name: permanent_wilting_point + quantity: gravimetric_water_content symbol: PWP - unit: "%" + unit: mass_percent + reported_unit: "%" domain: null description: Soil water content at -1500 kPa matric potential. functions: @@ -105,8 +109,9 @@ functions: - $ref: "#/$defs/cecNkp" outputs: $ref: "#/$defs/Dharumarajan2019WaterRetentionResult" - golden_tests: + verification_cases: - id: northern_study_mean_case + kind: calculated inputs: clay: 43.2 sand: 39.8 @@ -114,9 +119,7 @@ functions: expected: field_capacity: 29.7912 permanent_wilting_point: 18.9276 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated from the Table 3 coefficients at the Table 1 mean inputs. + rationale: Calculated from the Table 3 coefficients at the Table 1 mean inputs. edge_cases: [] documentation: notes: @@ -153,16 +156,15 @@ functions: - $ref: "#/$defs/clayNkp" outputs: $ref: "#/$defs/Dharumarajan2019WaterRetentionResult" - golden_tests: + verification_cases: - id: northern_study_mean_clay_case + kind: calculated inputs: clay: 43.2 expected: field_capacity: 30.2832 permanent_wilting_point: 19.157 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated from the Table 4 coefficients at the Table 1 mean clay content. + rationale: Calculated from the Table 4 coefficients at the Table 1 mean clay content. edge_cases: [] documentation: notes: @@ -203,8 +205,9 @@ functions: - $ref: "#/$defs/cecSkp" outputs: $ref: "#/$defs/Dharumarajan2019WaterRetentionResult" - golden_tests: + verification_cases: - id: southern_study_mean_case + kind: calculated inputs: clay: 31.5 sand: 53.5 @@ -212,9 +215,7 @@ functions: expected: field_capacity: 21.8169 permanent_wilting_point: 11.3054 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated from the Table 5 coefficients at the Table 2 mean inputs. + rationale: Calculated from the Table 5 coefficients at the Table 2 mean inputs. edge_cases: [] documentation: notes: @@ -248,16 +249,15 @@ functions: - $ref: "#/$defs/claySkp" outputs: $ref: "#/$defs/Dharumarajan2019WaterRetentionResult" - golden_tests: + verification_cases: - id: southern_study_mean_clay_case + kind: calculated inputs: clay: 31.5 expected: field_capacity: 22.0255 permanent_wilting_point: 11.503 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated from the Table 7 coefficients at the Table 2 mean clay content. + rationale: Calculated from the Table 7 coefficients at the Table 2 mean clay content. edge_cases: [] documentation: notes: @@ -294,21 +294,22 @@ functions: outputs: type: scalar name: infiltration_rate + quantity: infiltration_rate symbol: null - unit: mm/h + unit: millimeter_per_hour + reported_unit: mm/h domain: "Observed study range: 2.3 <= value <= 35" description: Soil infiltration rate measured with a double-ring infiltrometer. - golden_tests: + verification_cases: - id: balanced_texture_case + kind: calculated inputs: sand: 50.0 silt: 20.0 clay: 30.0 expected: infiltration_rate: 18.45 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated directly from the reported equation for a texture summing to 100%. + rationale: Calculated directly from the reported equation for a texture summing to 100%. edge_cases: [] documentation: notes: @@ -370,7 +371,7 @@ scientific_notes: | - Preserve the paper's `%` water-content unit without asserting an unsupported gravimetric or volumetric basis. - Evaluate in `f64`, do not round or clip outputs, and propagate NaN. - - Use directly calculated formula cases as golden tests because the source does + - Use directly calculated formula cases as verification tests because the source does not provide exact reference input-output pairs. ## Documented limitations diff --git a/specs/functions/ferrerjulia2004.yaml b/specs/functions/ferrerjulia2004.yaml index f06388d..d71526c 100644 --- a/specs/functions/ferrerjulia2004.yaml +++ b/specs/functions/ferrerjulia2004.yaml @@ -41,8 +41,10 @@ $defs: k_sat_mm_h: &k_sat_mm_h type: scalar name: k_sat + quantity: saturated_hydraulic_conductivity symbol: K_s - unit: mm/h + unit: millimeter_per_hour + reported_unit: mm/h domain: null description: Estimated saturated hydraulic conductivity. functions: @@ -52,16 +54,15 @@ functions: scope: {prediction_target: Saturated hydraulic conductivity., models: {h_theta: null, k_h: Campbell and Shiozawa (1994)}} inputs: [{$ref: '#/$defs/sand_percent'}, {$ref: '#/$defs/clay_percent'}] outputs: *k_sat_mm_h - golden_tests: + verification_cases: - id: reviewer_reference_vector + kind: calculated inputs: sand: 50 clay: 25 expected: k_sat: 0.025071690110308933 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. + rationale: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. edge_cases: [] documentation: {notes: [Reproduced in Section 4.2 for comparison with the new Spanish-soil PTFs.], warnings: []} implementation: @@ -73,16 +74,15 @@ functions: scope: {prediction_target: Saturated hydraulic conductivity., models: {h_theta: null, k_h: Saxton et al. (1986)}} inputs: [{$ref: '#/$defs/sand_percent'}, {$ref: '#/$defs/clay_positive_percent'}] outputs: *k_sat_mm_h - golden_tests: + verification_cases: - id: reviewer_reference_vector + kind: calculated inputs: sand: 50 clay: 25 expected: k_sat: 0.00022673430784327228 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. + rationale: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. edge_cases: [] documentation: {notes: [Reproduced in Section 4.2 for comparison with the new Spanish-soil PTFs.], warnings: [The denominator contains log10(clay) and the source does not state its valid domain or singularity policy.]} implementation: @@ -95,15 +95,14 @@ functions: scope: {prediction_target: Saturated hydraulic conductivity., models: {h_theta: null, k_h: Dane and Puckett (1994)}} inputs: [{$ref: '#/$defs/clay_percent'}] outputs: *k_sat_mm_h - golden_tests: + verification_cases: - id: reviewer_reference_vector + kind: calculated inputs: clay: 25 expected: k_sat: 8.302039828385373 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. + rationale: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. edge_cases: [] documentation: {notes: [Reproduced in Section 4.2 for comparison with the new Spanish-soil PTFs.], warnings: []} implementation: @@ -115,15 +114,14 @@ functions: scope: {prediction_target: Saturated hydraulic conductivity., models: {h_theta: null, k_h: Puckett et al. (1985)}} inputs: [{$ref: '#/$defs/clay_percent'}] outputs: *k_sat_mm_h - golden_tests: + verification_cases: - id: reviewer_reference_vector + kind: calculated inputs: clay: 25 expected: k_sat: 1.1257967371765654 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. + rationale: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. edge_cases: [] documentation: {notes: [Reproduced in Section 4.2 for comparison with the new Spanish-soil PTFs.], warnings: []} implementation: @@ -135,16 +133,15 @@ functions: scope: {prediction_target: Saturated hydraulic conductivity., models: {h_theta: null, k_h: Cosby et al. (1984)}} inputs: [{$ref: '#/$defs/sand_percent'}, {$ref: '#/$defs/clay_percent'}] outputs: *k_sat_mm_h - golden_tests: + verification_cases: - id: reviewer_reference_vector + kind: calculated inputs: sand: 50 clay: 25 expected: k_sat: 26.091830970918934 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. + rationale: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. edge_cases: [] documentation: {notes: [Reproduced in Section 4.2 for comparison with the new Spanish-soil PTFs.], warnings: []} implementation: @@ -157,15 +154,14 @@ functions: scope: {prediction_target: Saturated hydraulic conductivity for FAO 1974 Humic Acrisol., models: {h_theta: null, k_h: Exponential one-variable regression}} inputs: [{$ref: '#/$defs/sand_percent'}] outputs: *k_sat_mm_h - golden_tests: + verification_cases: - id: reviewer_reference_vector + kind: calculated inputs: sand: 50 expected: k_sat: 14.529879794648146 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. + rationale: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. edge_cases: [] documentation: {notes: ['Table 3; R^2 = 0.723.'], warnings: []} implementation: {variables: [{name: k_sat, expr: '1.5162 * exp(0.0452 * sand)'}]} @@ -175,17 +171,16 @@ functions: scope: {prediction_target: Saturated hydraulic conductivity for FAO 1974 Humic Acrisol., models: {h_theta: null, k_h: Linear three-variable regression}} inputs: [{$ref: '#/$defs/sand_percent'}, {$ref: '#/$defs/clay_percent'}, {$ref: '#/$defs/organic_matter_percent'}] outputs: *k_sat_mm_h - golden_tests: + verification_cases: - id: reviewer_reference_vector + kind: calculated inputs: sand: 50 clay: 25 organic_matter: 2.5 expected: k_sat: 19.563475 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. + rationale: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. edge_cases: [] documentation: {notes: ['Table 3; R^2 = 0.701.'], warnings: []} implementation: {variables: [{name: k_sat, expr: '16.019 + 0.345 * sand - 0.548 * clay - 0.00221 * organic_matter'}]} @@ -195,15 +190,14 @@ functions: scope: {prediction_target: Saturated hydraulic conductivity for FAO 1974 Calcic Cambisol., models: {h_theta: null, k_h: Exponential one-variable regression}} inputs: [{$ref: '#/$defs/sand_percent'}] outputs: *k_sat_mm_h - golden_tests: + verification_cases: - id: reviewer_reference_vector + kind: calculated inputs: sand: 50 expected: k_sat: 9.995162000935244 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. + rationale: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. edge_cases: [] documentation: {notes: ['Table 3; R^2 = 0.521.'], warnings: []} implementation: {variables: [{name: k_sat, expr: '1.043 * exp(0.0452 * sand)'}]} @@ -213,17 +207,16 @@ functions: scope: {prediction_target: Saturated hydraulic conductivity for FAO 1974 Calcic Cambisol., models: {h_theta: null, k_h: Linear three-variable regression}} inputs: [{$ref: '#/$defs/sand_percent'}, {$ref: '#/$defs/clay_percent'}, {$ref: '#/$defs/organic_matter_percent'}] outputs: *k_sat_mm_h - golden_tests: + verification_cases: - id: reviewer_reference_vector + kind: calculated inputs: sand: 50 clay: 25 organic_matter: 2.5 expected: k_sat: 16.51625 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. + rationale: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. edge_cases: [] documentation: {notes: ['Table 3; R^2 = 0.468.'], warnings: []} implementation: {variables: [{name: k_sat, expr: '-7.878 + 0.51 * sand - 0.00183 * clay - 0.424 * organic_matter'}]} @@ -233,15 +226,14 @@ functions: scope: {prediction_target: Saturated hydraulic conductivity for FAO 1974 Dystric Cambisol., models: {h_theta: null, k_h: Exponential one-variable regression}} inputs: [{$ref: '#/$defs/sand_percent'}] outputs: *k_sat_mm_h - golden_tests: + verification_cases: - id: reviewer_reference_vector + kind: calculated inputs: sand: 50 expected: k_sat: 13.413573870265868 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. + rationale: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. edge_cases: [] documentation: {notes: ['Table 3; R^2 = 0.750.'], warnings: []} implementation: {variables: [{name: k_sat, expr: '1.3182 * exp(0.0464 * sand)'}]} @@ -251,17 +243,16 @@ functions: scope: {prediction_target: Saturated hydraulic conductivity for FAO 1974 Dystric Cambisol., models: {h_theta: null, k_h: Linear three-variable regression}} inputs: [{$ref: '#/$defs/sand_percent'}, {$ref: '#/$defs/clay_percent'}, {$ref: '#/$defs/organic_matter_percent'}] outputs: *k_sat_mm_h - golden_tests: + verification_cases: - id: reviewer_reference_vector + kind: calculated inputs: sand: 50 clay: 25 organic_matter: 2.5 expected: k_sat: 20.79 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. + rationale: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. edge_cases: [] documentation: {notes: ['Table 3; R^2 = 0.779.'], warnings: []} implementation: {variables: [{name: k_sat, expr: '-5.125 + 0.631 * sand - 0.165 * clay - 0.604 * organic_matter'}]} @@ -271,15 +262,14 @@ functions: scope: {prediction_target: Saturated hydraulic conductivity for FAO 1974 Eutric Cambisol., models: {h_theta: null, k_h: Exponential one-variable regression}} inputs: [{$ref: '#/$defs/sand_percent'}] outputs: *k_sat_mm_h - golden_tests: + verification_cases: - id: reviewer_reference_vector + kind: calculated inputs: sand: 50 expected: k_sat: 11.570199173737363 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. + rationale: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. edge_cases: [] documentation: {notes: ['Table 3; R^2 = 0.734.'], warnings: []} implementation: {variables: [{name: k_sat, expr: '0.9356 * exp(0.0503 * sand)'}]} @@ -289,17 +279,16 @@ functions: scope: {prediction_target: Saturated hydraulic conductivity for FAO 1974 Eutric Cambisol., models: {h_theta: null, k_h: Linear three-variable regression}} inputs: [{$ref: '#/$defs/sand_percent'}, {$ref: '#/$defs/clay_percent'}, {$ref: '#/$defs/organic_matter_percent'}] outputs: *k_sat_mm_h - golden_tests: + verification_cases: - id: reviewer_reference_vector + kind: calculated inputs: sand: 50 clay: 25 organic_matter: 2.5 expected: k_sat: 19.5235 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. + rationale: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. edge_cases: [] documentation: {notes: ['Table 3; R^2 = 0.686.'], warnings: []} implementation: {variables: [{name: k_sat, expr: '-0.399 + 0.541 * sand - 0.243 * clay - 0.421 * organic_matter'}]} @@ -309,15 +298,14 @@ functions: scope: {prediction_target: Saturated hydraulic conductivity for FAO 1974 Gleyic Cambisol., models: {h_theta: null, k_h: Exponential one-variable regression}} inputs: [{$ref: '#/$defs/sand_percent'}] outputs: *k_sat_mm_h - golden_tests: + verification_cases: - id: reviewer_reference_vector + kind: calculated inputs: sand: 50 expected: k_sat: 6.559110321079926 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. + rationale: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. edge_cases: [] documentation: {notes: ['Table 3; R^2 = 0.807.'], warnings: []} implementation: {variables: [{name: k_sat, expr: '0.2336 * exp(0.0667 * sand)'}]} @@ -327,17 +315,16 @@ functions: scope: {prediction_target: Saturated hydraulic conductivity for FAO 1974 Gleyic Cambisol., models: {h_theta: null, k_h: Linear three-variable regression}} inputs: [{$ref: '#/$defs/sand_percent'}, {$ref: '#/$defs/clay_percent'}, {$ref: '#/$defs/organic_matter_percent'}] outputs: *k_sat_mm_h - golden_tests: + verification_cases: - id: reviewer_reference_vector + kind: calculated inputs: sand: 50 clay: 25 organic_matter: 2.5 expected: k_sat: 11.514 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. + rationale: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. edge_cases: [] documentation: {notes: ['Table 3; R^2 = 0.824.'], warnings: []} implementation: {variables: [{name: k_sat, expr: '-12.986 + 0.638 * sand - 0.244 * clay - 0.52 * organic_matter'}]} @@ -347,15 +334,14 @@ functions: scope: {prediction_target: Saturated hydraulic conductivity for FAO 1974 Humic Cambisol., models: {h_theta: null, k_h: Exponential one-variable regression}} inputs: [{$ref: '#/$defs/sand_percent'}] outputs: *k_sat_mm_h - golden_tests: + verification_cases: - id: reviewer_reference_vector + kind: calculated inputs: sand: 50 expected: k_sat: 13.820129957492508 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. + rationale: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. edge_cases: [] documentation: {notes: ['Table 3; R^2 = 0.592.'], warnings: []} implementation: {variables: [{name: k_sat, expr: '2.1622 * exp(0.0371 * sand)'}]} @@ -365,17 +351,16 @@ functions: scope: {prediction_target: Saturated hydraulic conductivity for FAO 1974 Humic Cambisol., models: {h_theta: null, k_h: Linear three-variable regression}} inputs: [{$ref: '#/$defs/sand_percent'}, {$ref: '#/$defs/clay_percent'}, {$ref: '#/$defs/organic_matter_percent'}] outputs: *k_sat_mm_h - golden_tests: + verification_cases: - id: reviewer_reference_vector + kind: calculated inputs: sand: 50 clay: 25 organic_matter: 2.5 expected: k_sat: 20.9029 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. + rationale: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. edge_cases: [] documentation: {notes: ['Table 3; R^2 = 0.632.'], warnings: []} implementation: {variables: [{name: k_sat, expr: '6.4429 + 0.475 * sand - 0.331 * clay - 0.406 * organic_matter'}]} @@ -385,15 +370,14 @@ functions: scope: {prediction_target: Saturated hydraulic conductivity for FAO 1974 Calcaric Fluvisol., models: {h_theta: null, k_h: Exponential one-variable regression}} inputs: [{$ref: '#/$defs/sand_percent'}] outputs: *k_sat_mm_h - golden_tests: + verification_cases: - id: reviewer_reference_vector + kind: calculated inputs: sand: 50 expected: k_sat: 10.959482760491376 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. + rationale: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. edge_cases: [] documentation: {notes: ['Table 3; R^2 = 0.772.'], warnings: []} implementation: {variables: [{name: k_sat, expr: '0.814 * exp(0.052 * sand)'}]} @@ -403,17 +387,16 @@ functions: scope: {prediction_target: Saturated hydraulic conductivity for FAO 1974 Calcaric Fluvisol., models: {h_theta: null, k_h: Linear three-variable regression}} inputs: [{$ref: '#/$defs/sand_percent'}, {$ref: '#/$defs/clay_percent'}, {$ref: '#/$defs/organic_matter_percent'}] outputs: *k_sat_mm_h - golden_tests: + verification_cases: - id: reviewer_reference_vector + kind: calculated inputs: sand: 50 clay: 25 organic_matter: 2.5 expected: k_sat: 13.101 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. + rationale: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. edge_cases: [] documentation: {notes: ['Table 3; R^2 = 0.792.'], warnings: []} implementation: {variables: [{name: k_sat, expr: '-15.779 + 0.657 * sand - 0.134 * clay - 0.248 * organic_matter'}]} @@ -423,15 +406,14 @@ functions: scope: {prediction_target: Saturated hydraulic conductivity for FAO 1974 Calcic Luvisol., models: {h_theta: null, k_h: Exponential one-variable regression}} inputs: [{$ref: '#/$defs/sand_percent'}] outputs: *k_sat_mm_h - golden_tests: + verification_cases: - id: reviewer_reference_vector + kind: calculated inputs: sand: 50 expected: k_sat: 6.931067612606957 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. + rationale: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. edge_cases: [] documentation: {notes: ['Table 3; R^2 = 0.412.'], warnings: [The paper reports weak fit and suggests missing clay-mineralogy information.]} implementation: {variables: [{name: k_sat, expr: '0.671 * exp(0.0467 * sand)'}]} @@ -441,17 +423,16 @@ functions: scope: {prediction_target: Saturated hydraulic conductivity for FAO 1974 Calcic Luvisol., models: {h_theta: null, k_h: Linear three-variable regression}} inputs: [{$ref: '#/$defs/sand_percent'}, {$ref: '#/$defs/clay_percent'}, {$ref: '#/$defs/organic_matter_percent'}] outputs: *k_sat_mm_h - golden_tests: + verification_cases: - id: reviewer_reference_vector + kind: calculated inputs: sand: 50 clay: 25 organic_matter: 2.5 expected: k_sat: 8.9332 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. + rationale: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. edge_cases: [] documentation: {notes: ['Table 3; R^2 = 0.473.'], warnings: [The paper reports weak fit and suggests missing clay-mineralogy information.]} implementation: {variables: [{name: k_sat, expr: '5.379 + 0.138 * sand - 0.131 * clay - 0.02832 * organic_matter'}]} @@ -461,15 +442,14 @@ functions: scope: {prediction_target: Saturated hydraulic conductivity for FAO 1974 Chromic Luvisol., models: {h_theta: null, k_h: Exponential one-variable regression}} inputs: [{$ref: '#/$defs/sand_percent'}] outputs: *k_sat_mm_h - golden_tests: + verification_cases: - id: reviewer_reference_vector + kind: calculated inputs: sand: 50 expected: k_sat: 10.61655727277843 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. + rationale: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. edge_cases: [] documentation: {notes: ['Table 3; R^2 = 0.534.'], warnings: []} implementation: {variables: [{name: k_sat, expr: '0.898 * exp(0.0494 * sand)'}]} @@ -479,17 +459,16 @@ functions: scope: {prediction_target: Saturated hydraulic conductivity for FAO 1974 Chromic Luvisol., models: {h_theta: null, k_h: Linear three-variable regression}} inputs: [{$ref: '#/$defs/sand_percent'}, {$ref: '#/$defs/clay_percent'}, {$ref: '#/$defs/organic_matter_percent'}] outputs: *k_sat_mm_h - golden_tests: + verification_cases: - id: reviewer_reference_vector + kind: calculated inputs: sand: 50 clay: 25 organic_matter: 2.5 expected: k_sat: 17.1465 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. + rationale: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. edge_cases: [] documentation: {notes: ['Table 3; R^2 = 0.557.'], warnings: []} implementation: {variables: [{name: k_sat, expr: '-10.171 + 0.576 * sand - 0.0075 * clay - 0.518 * organic_matter'}]} @@ -499,15 +478,14 @@ functions: scope: {prediction_target: Saturated hydraulic conductivity for FAO 1974 Gleyic Luvisol., models: {h_theta: null, k_h: Exponential one-variable regression}} inputs: [{$ref: '#/$defs/sand_percent'}] outputs: *k_sat_mm_h - golden_tests: + verification_cases: - id: reviewer_reference_vector + kind: calculated inputs: sand: 50 expected: k_sat: 7.049012285884197 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. + rationale: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. edge_cases: [] documentation: {notes: ['Table 3; R^2 = 0.876.'], warnings: []} implementation: {variables: [{name: k_sat, expr: '0.5395 * exp(0.0514 * sand)'}]} @@ -517,17 +495,16 @@ functions: scope: {prediction_target: Saturated hydraulic conductivity for FAO 1974 Gleyic Luvisol., models: {h_theta: null, k_h: Linear three-variable regression}} inputs: [{$ref: '#/$defs/sand_percent'}, {$ref: '#/$defs/clay_percent'}, {$ref: '#/$defs/organic_matter_percent'}] outputs: *k_sat_mm_h - golden_tests: + verification_cases: - id: reviewer_reference_vector + kind: calculated inputs: sand: 50 clay: 25 organic_matter: 2.5 expected: k_sat: 19.55 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. + rationale: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. edge_cases: [] documentation: {notes: ['Table 3; R^2 = 0.917.'], warnings: []} implementation: {variables: [{name: k_sat, expr: '10.28 + 0.449 * sand - 0.507 * clay - 0.202 * organic_matter'}]} @@ -537,15 +514,14 @@ functions: scope: {prediction_target: Saturated hydraulic conductivity for FAO 1974 Orthic Luvisol., models: {h_theta: null, k_h: Exponential one-variable regression}} inputs: [{$ref: '#/$defs/sand_percent'}] outputs: *k_sat_mm_h - golden_tests: + verification_cases: - id: reviewer_reference_vector + kind: calculated inputs: sand: 50 expected: k_sat: 12.04280911180458 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. + rationale: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. edge_cases: [] documentation: {notes: ['Table 3; R^2 = 0.676.'], warnings: []} implementation: {variables: [{name: k_sat, expr: '1.3958 * exp(0.0431 * sand)'}]} @@ -555,17 +531,16 @@ functions: scope: {prediction_target: Saturated hydraulic conductivity for FAO 1974 Orthic Luvisol., models: {h_theta: null, k_h: Linear three-variable regression}} inputs: [{$ref: '#/$defs/sand_percent'}, {$ref: '#/$defs/clay_percent'}, {$ref: '#/$defs/organic_matter_percent'}] outputs: *k_sat_mm_h - golden_tests: + verification_cases: - id: reviewer_reference_vector + kind: calculated inputs: sand: 50 clay: 25 organic_matter: 2.5 expected: k_sat: 21.40425 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. + rationale: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. edge_cases: [] documentation: {notes: ['Table 3; R^2 = 0.729.'], warnings: []} implementation: {variables: [{name: k_sat, expr: '4.829 + 0.499 * sand - 0.332 * clay - 0.0299 * organic_matter'}]} @@ -575,15 +550,14 @@ functions: scope: {prediction_target: Saturated hydraulic conductivity for FAO 1974 Ranker., models: {h_theta: null, k_h: Exponential one-variable regression}} inputs: [{$ref: '#/$defs/sand_percent'}] outputs: *k_sat_mm_h - golden_tests: + verification_cases: - id: reviewer_reference_vector + kind: calculated inputs: sand: 50 expected: k_sat: 12.423399456061938 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. + rationale: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. edge_cases: [] documentation: {notes: ['Table 3; R^2 = 0.726.'], warnings: []} implementation: {variables: [{name: k_sat, expr: '0.9847 * exp(0.0507 * sand)'}]} @@ -593,17 +567,16 @@ functions: scope: {prediction_target: Saturated hydraulic conductivity for FAO 1974 Ranker., models: {h_theta: null, k_h: Linear three-variable regression}} inputs: [{$ref: '#/$defs/sand_percent'}, {$ref: '#/$defs/clay_percent'}, {$ref: '#/$defs/organic_matter_percent'}] outputs: *k_sat_mm_h - golden_tests: + verification_cases: - id: reviewer_reference_vector + kind: calculated inputs: sand: 50 clay: 25 organic_matter: 2.5 expected: k_sat: 22.4515 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. + rationale: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. edge_cases: [] documentation: {notes: ['Table 3; R^2 = 0.717.'], warnings: []} implementation: {variables: [{name: k_sat, expr: '-8.621 + 0.682 * sand - 0.0771 * clay - 0.44 * organic_matter'}]} @@ -613,15 +586,14 @@ functions: scope: {prediction_target: Saturated hydraulic conductivity for FAO 1974 Calcaric Regosol., models: {h_theta: null, k_h: Exponential one-variable regression}} inputs: [{$ref: '#/$defs/sand_percent'}] outputs: *k_sat_mm_h - golden_tests: + verification_cases: - id: reviewer_reference_vector + kind: calculated inputs: sand: 50 expected: k_sat: 12.267374733833096 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. + rationale: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. edge_cases: [] documentation: {notes: ['Table 3; R^2 = 0.655.'], warnings: []} implementation: {variables: [{name: k_sat, expr: '0.8931 * exp(0.0524 * sand)'}]} @@ -631,17 +603,16 @@ functions: scope: {prediction_target: Saturated hydraulic conductivity for FAO 1974 Calcaric Regosol., models: {h_theta: null, k_h: Linear three-variable regression}} inputs: [{$ref: '#/$defs/sand_percent'}, {$ref: '#/$defs/clay_percent'}, {$ref: '#/$defs/organic_matter_percent'}] outputs: *k_sat_mm_h - golden_tests: + verification_cases: - id: reviewer_reference_vector + kind: calculated inputs: sand: 50 clay: 25 organic_matter: 2.5 expected: k_sat: 20.532 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. + rationale: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. edge_cases: [] documentation: {notes: ['Table 3; R^2 = 0.705.'], warnings: []} implementation: {variables: [{name: k_sat, expr: '1.522 + 0.537 * sand - 0.284 * clay - 0.296 * organic_matter'}]} @@ -651,15 +622,14 @@ functions: scope: {prediction_target: Saturated hydraulic conductivity for FAO 1974 Dystric Regosol., models: {h_theta: null, k_h: Exponential one-variable regression}} inputs: [{$ref: '#/$defs/sand_percent'}] outputs: *k_sat_mm_h - golden_tests: + verification_cases: - id: reviewer_reference_vector + kind: calculated inputs: sand: 50 expected: k_sat: 12.081401313183196 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. + rationale: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. edge_cases: [] documentation: {notes: ['Table 3; R^2 = 0.834.'], warnings: []} implementation: {variables: [{name: k_sat, expr: '1.096 * exp(0.048 * sand)'}]} @@ -669,17 +639,16 @@ functions: scope: {prediction_target: Saturated hydraulic conductivity for FAO 1974 Dystric Regosol., models: {h_theta: null, k_h: Linear three-variable regression}} inputs: [{$ref: '#/$defs/sand_percent'}, {$ref: '#/$defs/clay_percent'}, {$ref: '#/$defs/organic_matter_percent'}] outputs: *k_sat_mm_h - golden_tests: + verification_cases: - id: reviewer_reference_vector + kind: calculated inputs: sand: 50 clay: 25 organic_matter: 2.5 expected: k_sat: 17.1335 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. + rationale: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. edge_cases: [] documentation: {notes: ['Table 3; R^2 = 0.862.'], warnings: []} implementation: {variables: [{name: k_sat, expr: '-0.784 + 0.596 * sand - 0.431 * clay - 0.443 * organic_matter'}]} @@ -689,15 +658,14 @@ functions: scope: {prediction_target: Saturated hydraulic conductivity for FAO 1974 Eutric Regosol., models: {h_theta: null, k_h: Exponential one-variable regression}} inputs: [{$ref: '#/$defs/sand_percent'}] outputs: *k_sat_mm_h - golden_tests: + verification_cases: - id: reviewer_reference_vector + kind: calculated inputs: sand: 50 expected: k_sat: 13.674049986082728 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. + rationale: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. edge_cases: [] documentation: {notes: ['Table 3; R^2 = 0.824.'], warnings: []} implementation: {variables: [{name: k_sat, expr: '1.0005 * exp(0.0523 * sand)'}]} @@ -707,17 +675,16 @@ functions: scope: {prediction_target: Saturated hydraulic conductivity for FAO 1974 Eutric Regosol., models: {h_theta: null, k_h: Linear three-variable regression}} inputs: [{$ref: '#/$defs/sand_percent'}, {$ref: '#/$defs/clay_percent'}, {$ref: '#/$defs/organic_matter_percent'}] outputs: *k_sat_mm_h - golden_tests: + verification_cases: - id: reviewer_reference_vector + kind: calculated inputs: sand: 50 clay: 25 organic_matter: 2.5 expected: k_sat: 24.06375 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. + rationale: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. edge_cases: [] documentation: {notes: ['Table 3; R^2 = 0.702.'], warnings: []} implementation: {variables: [{name: k_sat, expr: '-5.5 + 0.647 * sand - 0.00325 * clay - 1.082 * organic_matter'}]} @@ -727,15 +694,14 @@ functions: scope: {prediction_target: Saturated hydraulic conductivity for FAO 1974 Rendzina., models: {h_theta: null, k_h: Exponential one-variable regression}} inputs: [{$ref: '#/$defs/sand_percent'}] outputs: *k_sat_mm_h - golden_tests: + verification_cases: - id: reviewer_reference_vector + kind: calculated inputs: sand: 50 expected: k_sat: 12.515763717130312 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. + rationale: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. edge_cases: [] documentation: {notes: ['Table 3; horizon-level R^2 = 0.221; the paper separately reports profile-average R^2 = 0.727 without coefficients.'], warnings: [The horizon-level regression has weak fit and is affected by converted qualitative conductivity values.]} implementation: {variables: [{name: k_sat, expr: '2.4645 * exp(0.0325 * sand)'}]} @@ -745,17 +711,16 @@ functions: scope: {prediction_target: Saturated hydraulic conductivity for FAO 1974 Rendzina., models: {h_theta: null, k_h: Linear three-variable regression}} inputs: [{$ref: '#/$defs/sand_percent'}, {$ref: '#/$defs/clay_percent'}, {$ref: '#/$defs/organic_matter_percent'}] outputs: *k_sat_mm_h - golden_tests: + verification_cases: - id: reviewer_reference_vector + kind: calculated inputs: sand: 50 clay: 25 organic_matter: 2.5 expected: k_sat: 16.2905 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. + rationale: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. edge_cases: [] documentation: {notes: ['Table 3; R^2 = 0.340.'], warnings: [The regression has weak fit and is affected by converted qualitative conductivity values.]} implementation: {variables: [{name: k_sat, expr: '23.288 + 0.175 * sand - 0.602 * clay - 0.279 * organic_matter'}]} @@ -765,15 +730,14 @@ functions: scope: {prediction_target: Saturated hydraulic conductivity for FAO 1974 Gleyic Solonchak., models: {h_theta: null, k_h: Exponential one-variable regression}} inputs: [{$ref: '#/$defs/sand_percent'}] outputs: *k_sat_mm_h - golden_tests: + verification_cases: - id: reviewer_reference_vector + kind: calculated inputs: sand: 50 expected: k_sat: 4.883324693640427 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. + rationale: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. edge_cases: [] documentation: {notes: ['Table 3 prints R^2 = 0.399; the prose later states 0.435.'], warnings: [The source gives inconsistent R^2 values for this regression.]} implementation: {variables: [{name: k_sat, expr: '0.7602 * exp(0.0372 * sand)'}]} @@ -783,17 +747,16 @@ functions: scope: {prediction_target: Saturated hydraulic conductivity for FAO 1974 Gleyic Solonchak., models: {h_theta: null, k_h: Linear three-variable regression}} inputs: [{$ref: '#/$defs/sand_percent'}, {$ref: '#/$defs/clay_percent'}, {$ref: '#/$defs/organic_matter_percent'}] outputs: *k_sat_mm_h - golden_tests: + verification_cases: - id: reviewer_reference_vector + kind: calculated inputs: sand: 50 clay: 25 organic_matter: 2.5 expected: k_sat: 10.53583 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. + rationale: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. edge_cases: [] documentation: {notes: ['Table 3; R^2 = 0.682.', 'Human review corrected the anomalous printed intercept from -90917 to -0.90917.'], warnings: [The implementation intentionally differs from the original published Table 3, which prints the intercept as -90917.]} implementation: {variables: [{name: k_sat, expr: '-0.90917 + 0.328 * sand - 0.173 * clay - 0.252 * organic_matter'}]} @@ -804,15 +767,14 @@ functions: scope: {prediction_target: Saturated hydraulic conductivity for the combined Spanish-soil dataset., models: {h_theta: null, k_h: Exponential one-variable regression}} inputs: [{$ref: '#/$defs/sand_percent'}] outputs: *k_sat_mm_h - golden_tests: + verification_cases: - id: reviewer_reference_vector + kind: calculated inputs: sand: 50 expected: k_sat: 10.714718864969111 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. + rationale: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. edge_cases: [] documentation: {notes: ['Section 5.1; 3172 horizons; R^2 = 0.696.'], warnings: [The paper recommends use when data are statistically similar to its Spanish-soil calibration data.]} implementation: {variables: [{name: k_sat, expr: '0.920 * exp(0.0491 * sand)'}]} @@ -822,17 +784,16 @@ functions: scope: {prediction_target: Saturated hydraulic conductivity for the combined Spanish-soil dataset., models: {h_theta: null, k_h: Linear three-variable regression}} inputs: [{$ref: '#/$defs/sand_percent'}, {$ref: '#/$defs/clay_percent'}, {$ref: '#/$defs/organic_matter_percent'}] outputs: *k_sat_mm_h - golden_tests: + verification_cases: - id: reviewer_reference_vector + kind: calculated inputs: sand: 50 clay: 25 organic_matter: 2.5 expected: k_sat: 20.06325 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. + rationale: Synthetic finite-domain verification vector selected during human review and evaluated directly from the documented formula. edge_cases: [] documentation: {notes: ['Section 5.1; 3172 horizons; R^2 = 0.715.'], warnings: [The paper recommends use when data are statistically similar to its Spanish-soil calibration data.]} implementation: {variables: [{name: k_sat, expr: '-4.994 + 0.56728 * sand - 0.131 * clay - 0.0127 * organic_matter'}]} @@ -876,7 +837,7 @@ scientific_notes: | NaN and infinities with NumPy-compatible arithmetic. In particular, retain negative results from the linear regressions rather than silently replacing them with zero. - - Use the synthetic finite-domain vectors recorded in `golden_tests` as + - Use the synthetic finite-domain vectors recorded in `verification_cases` as implementation regression tests. Their expected values are direct formula evaluations and are not presented as observations from the article. - No separate edge-case vectors are required beyond input-domain validation diff --git a/specs/functions/hodnett2002.yaml b/specs/functions/hodnett2002.yaml index 5f2c177..a64ac2e 100644 --- a/specs/functions/hodnett2002.yaml +++ b/specs/functions/hodnett2002.yaml @@ -76,27 +76,36 @@ functions: name: Hodnett2002PTFResult fields: - name: alpha + quantity: van_genuchten_alpha symbol: alpha - unit: kPa^-1 + unit: inverse_kilopascal + reported_unit: kPa^-1 domain: value > 0 description: Shape parameter of the van Genuchten water-retention model. - name: n + quantity: van_genuchten_n symbol: n unit: dimensionless + reported_unit: dimensionless domain: value > 0 description: Shape parameter controlling water-retention curve steepness. - name: theta_s + quantity: volumetric_water_content symbol: theta_s - unit: m^3/m^3 + unit: volume_fraction + reported_unit: m^3/m^3 domain: null description: Saturated volumetric soil water content. - name: theta_r + quantity: volumetric_water_content symbol: theta_r - unit: m^3/m^3 + unit: volume_fraction + reported_unit: m^3/m^3 domain: null description: Residual volumetric soil water content. - golden_tests: + verification_cases: - id: calibration_dataset_mean_properties + kind: calculated inputs: sand: 39.2 silt: 24.2 @@ -110,9 +119,7 @@ functions: n: 1.36739326352383 theta_s: 0.4993353 theta_r: 0.21344216 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: >- + rationale: >- Calculated from the reoptimised Step 3 coefficients in Table 8 using the calibration-set means in Table 3 and the table's factor-of-100 note. edge_cases: [] diff --git a/specs/functions/jabro1992.yaml b/specs/functions/jabro1992.yaml index 6a1ff40..5aadece 100644 --- a/specs/functions/jabro1992.yaml +++ b/specs/functions/jabro1992.yaml @@ -43,51 +43,49 @@ functions: outputs: type: scalar name: k_sat + quantity: saturated_hydraulic_conductivity symbol: Ks - unit: m/s + unit: meter_per_second + reported_unit: m/s domain: value >= 0 description: Saturated hydraulic conductivity. - golden_tests: + verification_cases: - id: loamy_sand_min_bd + kind: calculated inputs: silt: 10.0 clay: 5.0 bulk_density: 1.26 expected: k_sat: 0.0003849640675896946 - rtol: 1.0e-08 - atol: 1.0e-12 - notes: Calculated from equation (3) and unit conversion. + rationale: Calculated from equation (3) and unit conversion. - id: loam + kind: calculated inputs: silt: 38.72 clay: 11.05 bulk_density: 1.42 expected: k_sat: 9.804037952717678e-06 - rtol: 1.0e-08 - atol: 1.0e-12 - notes: Calculated from equation (3) and unit conversion. + rationale: Calculated from equation (3) and unit conversion. - id: silty_clay_loam_max_silt + kind: calculated inputs: silt: 52.0 clay: 30.0 bulk_density: 1.97 expected: k_sat: 7.292435947882127e-09 - rtol: 1.0e-08 - atol: 1.0e-12 - notes: Calculated from equation (3) and unit conversion. + rationale: Calculated from equation (3) and unit conversion. - id: sandy_clay_min_silt_max_clay + kind: calculated inputs: silt: 0.2 clay: 44.0 bulk_density: 1.61 expected: k_sat: 2.032824027706267e-05 - rtol: 1.0e-08 - atol: 1.0e-12 - notes: Calculated from equation (3) and unit conversion. + rationale: Calculated from equation (3) and unit conversion. edge_cases: - id: non_positive_silt_or_clay inputs: diff --git a/specs/functions/li2007.yaml b/specs/functions/li2007.yaml index 28a3b06..49db86b 100644 --- a/specs/functions/li2007.yaml +++ b/specs/functions/li2007.yaml @@ -55,27 +55,36 @@ functions: name: Li2007PTFResult fields: - name: theta_s + quantity: volumetric_water_content symbol: theta_s - unit: cm^3/cm^3 + unit: volume_fraction + reported_unit: cm^3/cm^3 domain: value >= 0 description: Saturated water content. - name: a_vg + quantity: van_genuchten_alpha symbol: alpha - unit: cm^-1 + unit: inverse_centimeter + reported_unit: cm^-1 domain: value >= 0 description: Van Genuchten alpha parameter, inversely related to air-entry suction. - name: n_vg + quantity: van_genuchten_n symbol: n unit: dimensionless + reported_unit: dimensionless domain: value > 1 description: Van Genuchten n parameter that characterizes pore-size distribution. - name: k_sat + quantity: saturated_hydraulic_conductivity symbol: Ks - unit: m/s + unit: meter_per_second + reported_unit: m/s domain: value >= 0 description: Saturated hydraulic conductivity. - golden_tests: + verification_cases: - id: loamy_sand + kind: calculated inputs: sand: 85.0 silt: 10.0 @@ -87,10 +96,9 @@ functions: a_vg: 0.9491464758307142 n_vg: 1.1657804980997006 k_sat: 6.549110367333547e-06 - rtol: 1.0e-08 - atol: 1.0e-12 - notes: Calculated from Table 6 formulas and unit conversion. + rationale: Calculated from Table 6 formulas and unit conversion. - id: loam + kind: calculated inputs: sand: 50.23 silt: 38.72 @@ -102,10 +110,9 @@ functions: a_vg: 0.009519989841950734 n_vg: 1.1806286355149054 k_sat: 4.5117324656202257e-07 - rtol: 1.0e-08 - atol: 1.0e-12 - notes: Calculated from Table 6 formulas and unit conversion. + rationale: Calculated from Table 6 formulas and unit conversion. - id: silty_clay + kind: calculated inputs: sand: 12.88 silt: 60.0 @@ -117,9 +124,7 @@ functions: a_vg: 0.0018530400762371828 n_vg: 1.2080428739797433 k_sat: 1.5151432632107234e-06 - rtol: 1.0e-08 - atol: 1.0e-12 - notes: Calculated from Table 6 formulas and unit conversion. + rationale: Calculated from Table 6 formulas and unit conversion. edge_cases: - id: non_positive_log_input inputs: diff --git a/specs/functions/mayr1999.yaml b/specs/functions/mayr1999.yaml index f607ae0..57a7a93 100644 --- a/specs/functions/mayr1999.yaml +++ b/specs/functions/mayr1999.yaml @@ -62,23 +62,30 @@ functions: name: Mayr1999PTFResult fields: - name: a_hc + quantity: matric_potential symbol: a - unit: cm H2O + unit: centimeter_water_head + reported_unit: cm H2O domain: value > 0 description: >- Hutson-Cass fitting parameter with pressure-head dimensions. - name: b_hc + quantity: hutson_cass_b symbol: b unit: dimensionless + reported_unit: dimensionless domain: value > 0 description: Hutson-Cass fitting parameter controlling retention-curve shape. - name: theta_s + quantity: volumetric_water_content symbol: theta_s - unit: m^3/m^3 + unit: volume_fraction + reported_unit: m^3/m^3 domain: 0 <= value <= 1 description: Volumetric soil water content at saturation. - golden_tests: + verification_cases: - id: representative_loam + kind: calculated inputs: sand: 40.0 silt: 40.0 @@ -89,10 +96,9 @@ functions: a_hc: 9.19922782578422 b_hc: 8.52432443785475 theta_s: 0.48733161333 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated from the Table 2 regressions using base-10 logarithms. + rationale: Calculated from the Table 2 regressions using base-10 logarithms. - id: representative_clay + kind: calculated inputs: sand: 20.0 silt: 20.0 @@ -103,10 +109,9 @@ functions: a_hc: 14.5305137640911 b_hc: 18.9375141190925 theta_s: 0.52145684162 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated from the Table 2 regressions using base-10 logarithms. + rationale: Calculated from the Table 2 regressions using base-10 logarithms. - id: representative_sand + kind: calculated inputs: sand: 90.0 silt: 5.0 @@ -117,9 +122,7 @@ functions: a_hc: 2.40216699550362 b_hc: 3.2690464531591 theta_s: 0.4209756915 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated from the Table 2 regressions using base-10 logarithms. + rationale: Calculated from the Table 2 regressions using base-10 logarithms. edge_cases: [] documentation: notes: @@ -164,7 +167,7 @@ scientific_notes: | The paper writes `log(a)` and `log(1/b)` without naming the logarithm base. Human review selected base 10 for implementation. Consequently, - `a_hc = 10^log10(a)` and `b_hc = 10^(-log10(1/b))`. The golden cases are + `a_hc = 10^log10(a)` and `b_hc = 10^(-log10(1/b))`. The verification cases are calculated from this interpretation and are not published worked examples. ## Published regressions diff --git a/specs/functions/oosterveld1980.yaml b/specs/functions/oosterveld1980.yaml index 7612795..853dd18 100644 --- a/specs/functions/oosterveld1980.yaml +++ b/specs/functions/oosterveld1980.yaml @@ -62,19 +62,20 @@ functions: outputs: type: scalar name: field_capacity_tension + quantity: matric_potential symbol: T_Pr - unit: kPa + unit: kilopascal + reported_unit: kPa domain: value > 0 description: Predicted tension corresponding to cylinder-method field capacity. - golden_tests: + verification_cases: - id: table_1_loamy_sand + kind: calculated inputs: clay: 6.6 expected: field_capacity_tension: 11.8540994164288 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: >- + rationale: >- Direct evaluation of Equation 1; Table I reports the value rounded to 11.8 kPa. edge_cases: @@ -111,12 +112,15 @@ functions: outputs: type: scalar name: moisture_content + quantity: gravimetric_water_content symbol: theta - unit: '% mass' + unit: mass_percent + reported_unit: '% mass' domain: null description: Predicted soil-moisture content in percent by weight. - golden_tests: + verification_cases: - id: table_1_loamy_sand_inputs + kind: calculated inputs: clay: 6.6 sand: 86.3 @@ -124,9 +128,7 @@ functions: tension: 11.8 expected: moisture_content: 8.2782468100413 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: >- + rationale: >- Direct evaluation of the corrected Equation 2 at the Table I loamy-sand inputs and its rounded field-capacity tension. Table I instead reports the separate Equation 3 field-capacity prediction. @@ -167,21 +169,22 @@ functions: outputs: type: scalar name: field_capacity_moisture + quantity: gravimetric_water_content symbol: theta_fc - unit: '% mass' + unit: mass_percent + reported_unit: '% mass' domain: null description: Predicted soil-moisture content at field capacity in percent by weight. - golden_tests: + verification_cases: - id: table_1_loamy_sand + kind: calculated inputs: clay: 6.6 sand: 86.3 mean_depth: 105 expected: field_capacity_moisture: 8.14707947394088 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: >- + rationale: >- Direct evaluation of corrected Equation 3; Table I reports the value rounded to 8.1%. edge_cases: @@ -221,21 +224,22 @@ functions: outputs: type: scalar name: wilting_point_moisture + quantity: gravimetric_water_content symbol: theta_1500_kPa - unit: '% mass' + unit: mass_percent + reported_unit: '% mass' domain: null description: Predicted soil-moisture content at 1500 kPa in percent by weight. - golden_tests: + verification_cases: - id: table_1_loamy_sand + kind: calculated inputs: clay: 6.6 sand: 86.3 mean_depth: 105 expected: wilting_point_moisture: 1.3942 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: >- + rationale: >- Direct evaluation of Equation 4; Table I reports the value rounded to 1.4%. edge_cases: @@ -276,23 +280,24 @@ functions: outputs: type: scalar name: available_moisture + quantity: gravimetric_water_content symbol: null - unit: '% mass' + unit: mass_percent + reported_unit: '% mass' domain: null description: >- Predicted field-capacity moisture minus predicted wilting-point moisture, in percentage points by weight. - golden_tests: + verification_cases: - id: table_1_loamy_sand + kind: calculated inputs: clay: 6.6 sand: 86.3 mean_depth: 105 expected: available_moisture: 6.75287947394088 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: >- + rationale: >- Direct evaluation of Equations 3 and 4 followed by subtraction; Table I reports the value rounded to 6.7%. edge_cases: @@ -338,7 +343,7 @@ scientific_notes: | explicitly excludes a bulk-density factor and percent-to-fraction conversion. - Use volume 22 in the citation, following the prepared source's canonical provenance; the appended transcription prints volume 21. - - Golden values are unrounded direct evaluations of the printed or corrected + - Verification values are unrounded direct evaluations of the printed or corrected equations. Rounded Table I predictions provide source-native cross-checks. ## Documented limitations diff --git a/specs/functions/pidgeon1972.yaml b/specs/functions/pidgeon1972.yaml index 9ec7f0c..ed599df 100644 --- a/specs/functions/pidgeon1972.yaml +++ b/specs/functions/pidgeon1972.yaml @@ -40,21 +40,22 @@ functions: outputs: type: scalar name: field_capacity + quantity: gravimetric_water_content symbol: FC - unit: '% w/w' + unit: mass_percent + reported_unit: '% w/w' domain: null description: Gravimetric field capacity. - golden_tests: + verification_cases: - id: regression_case + kind: calculated inputs: silt: 30.0 clay: 20.0 organic_matter: 2.0 expected: field_capacity: 21.26 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated from Table 3 using the reviewed 1.54 organic-matter coefficient. + rationale: Calculated from Table 3 using the reviewed 1.54 organic-matter coefficient. edge_cases: [] documentation: notes: @@ -84,19 +85,20 @@ functions: outputs: type: scalar name: field_capacity + quantity: gravimetric_water_content symbol: FC - unit: '% w/w' + unit: mass_percent + reported_unit: '% w/w' domain: null description: Gravimetric field capacity. - golden_tests: + verification_cases: - id: regression_case + kind: calculated inputs: sand: 50.0 expected: field_capacity: 23.66 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated from Table 3. + rationale: Calculated from Table 3. edge_cases: [] documentation: notes: [] @@ -130,20 +132,21 @@ functions: outputs: type: scalar name: field_capacity + quantity: gravimetric_water_content symbol: FC - unit: '% w/w' + unit: mass_percent + reported_unit: '% w/w' domain: null description: Gravimetric field capacity. - golden_tests: + verification_cases: - id: regression_case + kind: calculated inputs: sand: 50.0 organic_matter: 2.0 expected: field_capacity: 23.27 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated from Table 3. + rationale: Calculated from Table 3. edge_cases: [] documentation: notes: [] @@ -177,20 +180,21 @@ functions: outputs: type: scalar name: field_capacity + quantity: volumetric_water_content symbol: FCvol - unit: '% v/v' + unit: volume_percent + reported_unit: '% v/v' domain: null description: Volumetric field capacity. - golden_tests: + verification_cases: - id: regression_case + kind: calculated inputs: sand: 50.0 organic_matter: 2.0 expected: field_capacity: 31.19 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated from Table 3. + rationale: Calculated from Table 3. edge_cases: [] documentation: notes: [] @@ -229,21 +233,22 @@ functions: outputs: type: scalar name: permanent_wilting_point + quantity: gravimetric_water_content symbol: PWP - unit: '% w/w' + unit: mass_percent + reported_unit: '% w/w' domain: null description: Permanent wilting point. - golden_tests: + verification_cases: - id: regression_case + kind: calculated inputs: silt: 30.0 clay: 20.0 organic_matter: 2.0 expected: permanent_wilting_point: 11.11 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated from Table 3. + rationale: Calculated from Table 3. edge_cases: [] documentation: notes: [] @@ -272,19 +277,20 @@ functions: outputs: type: scalar name: permanent_wilting_point + quantity: gravimetric_water_content symbol: PWP - unit: '% w/w' + unit: mass_percent + reported_unit: '% w/w' domain: null description: Permanent wilting point. - golden_tests: + verification_cases: - id: regression_case + kind: calculated inputs: sand: 50.0 expected: permanent_wilting_point: 13.91 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated from Table 3. + rationale: Calculated from Table 3. edge_cases: [] documentation: notes: [] @@ -318,20 +324,21 @@ functions: outputs: type: scalar name: permanent_wilting_point + quantity: gravimetric_water_content symbol: PWP - unit: '% w/w' + unit: mass_percent + reported_unit: '% w/w' domain: null description: Permanent wilting point. - golden_tests: + verification_cases: - id: regression_case + kind: calculated inputs: sand: 50.0 organic_matter: 2.0 expected: permanent_wilting_point: 15.28 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated from Table 3. + rationale: Calculated from Table 3. edge_cases: [] documentation: notes: [] @@ -365,20 +372,21 @@ functions: outputs: type: scalar name: available_water_capacity + quantity: available_water_capacity symbol: AWC - unit: mm/m + unit: millimeter_per_meter + reported_unit: mm/m domain: null description: Available water capacity. - golden_tests: + verification_cases: - id: regression_case + kind: calculated inputs: clay: 20.0 organic_matter: 2.0 expected: available_water_capacity: 151.48 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated from Table 3. + rationale: Calculated from Table 3. edge_cases: [] documentation: notes: [] @@ -412,20 +420,21 @@ functions: outputs: type: scalar name: available_water_capacity + quantity: available_water_capacity symbol: AWC - unit: mm/m + unit: millimeter_per_meter + reported_unit: mm/m domain: null description: Available water capacity. - golden_tests: + verification_cases: - id: regression_case + kind: calculated inputs: sand: 50.0 organic_matter: 2.0 expected: available_water_capacity: 109.24 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated from Table 3. + rationale: Calculated from Table 3. edge_cases: [] documentation: notes: [] @@ -454,19 +463,20 @@ functions: outputs: type: scalar name: available_water_capacity + quantity: available_water_capacity symbol: AWC - unit: mm/m + unit: millimeter_per_meter + reported_unit: mm/m domain: null description: Available water capacity. - golden_tests: + verification_cases: - id: regression_case + kind: calculated inputs: coarse_sand: 20.0 expected: available_water_capacity: 115.1 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated from Table 3. + rationale: Calculated from Table 3. edge_cases: [] documentation: notes: [] @@ -495,19 +505,20 @@ functions: outputs: type: scalar name: available_water_capacity + quantity: available_water_capacity symbol: AWC - unit: mm/m + unit: millimeter_per_meter + reported_unit: mm/m domain: null description: Available water capacity. - golden_tests: + verification_cases: - id: regression_case + kind: calculated inputs: fine_sand: 20.0 expected: available_water_capacity: 119.9 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated from Table 3. + rationale: Calculated from Table 3. edge_cases: [] documentation: notes: [] @@ -536,19 +547,20 @@ functions: outputs: type: scalar name: available_water_capacity + quantity: available_water_capacity symbol: AWC - unit: mm/m + unit: millimeter_per_meter + reported_unit: mm/m domain: null description: Available water capacity. - golden_tests: + verification_cases: - id: regression_case + kind: calculated inputs: very_fine_sand: 10.0 expected: available_water_capacity: 112.7 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated from Table 3. + rationale: Calculated from Table 3. edge_cases: [] documentation: notes: [] @@ -588,21 +600,22 @@ functions: outputs: type: scalar name: extended_available_water_capacity + quantity: available_water_capacity symbol: EAWC - unit: mm/m + unit: millimeter_per_meter + reported_unit: mm/m domain: null description: Extended available water capacity. - golden_tests: + verification_cases: - id: regression_case + kind: calculated inputs: silt: 30.0 clay: 20.0 organic_matter: 2.0 expected: extended_available_water_capacity: 16.12 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated from Table 3. + rationale: Calculated from Table 3. edge_cases: [] documentation: notes: [] @@ -631,19 +644,20 @@ functions: outputs: type: scalar name: extended_available_water_capacity + quantity: available_water_capacity symbol: EAWC - unit: mm/m + unit: millimeter_per_meter + reported_unit: mm/m domain: null description: Extended available water capacity. - golden_tests: + verification_cases: - id: regression_case + kind: calculated inputs: sand: 50.0 expected: extended_available_water_capacity: 51.7 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated from Table 3. + rationale: Calculated from Table 3. edge_cases: [] documentation: notes: [] @@ -677,20 +691,21 @@ functions: outputs: type: scalar name: extended_available_water_capacity + quantity: available_water_capacity symbol: EAWC - unit: mm/m + unit: millimeter_per_meter + reported_unit: mm/m domain: null description: Extended available water capacity. - golden_tests: + verification_cases: - id: regression_case + kind: calculated inputs: sand: 50.0 organic_matter: 2.0 expected: extended_available_water_capacity: 56.26 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated from Table 3. + rationale: Calculated from Table 3. edge_cases: [] documentation: notes: [] @@ -725,20 +740,21 @@ functions: outputs: type: scalar name: extended_available_water_capacity + quantity: available_water_capacity symbol: EAWC - unit: mm/m + unit: millimeter_per_meter + reported_unit: mm/m domain: null description: Extended available water capacity. - golden_tests: + verification_cases: - id: regression_case + kind: calculated inputs: coarse_sand: 20.0 organic_matter: 2.0 expected: extended_available_water_capacity: 53.72 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated from Table 3. + rationale: Calculated from Table 3. edge_cases: [] documentation: notes: [] @@ -773,20 +789,21 @@ functions: outputs: type: scalar name: extended_available_water_capacity + quantity: available_water_capacity symbol: EAWC - unit: mm/m + unit: millimeter_per_meter + reported_unit: mm/m domain: null description: Extended available water capacity. - golden_tests: + verification_cases: - id: regression_case + kind: calculated inputs: fine_sand: 20.0 organic_matter: 2.0 expected: extended_available_water_capacity: 59.58 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated from Table 3. + rationale: Calculated from Table 3. edge_cases: [] documentation: notes: [] diff --git a/specs/functions/puckett1985.yaml b/specs/functions/puckett1985.yaml index 72022bb..22562b0 100644 --- a/specs/functions/puckett1985.yaml +++ b/specs/functions/puckett1985.yaml @@ -52,62 +52,85 @@ functions: name: Puckett1985PTFResult fields: - name: theta_0 + quantity: volumetric_water_content symbol: theta_0 - unit: cm^3/cm^3 + unit: volume_fraction + reported_unit: cm^3/cm^3 domain: null description: Volumetric water content at 0 kPa. - name: theta_1 + quantity: volumetric_water_content symbol: theta_1 - unit: cm^3/cm^3 + unit: volume_fraction + reported_unit: cm^3/cm^3 domain: null description: Volumetric water content at -1 kPa. - name: theta_5 + quantity: volumetric_water_content symbol: theta_5 - unit: cm^3/cm^3 + unit: volume_fraction + reported_unit: cm^3/cm^3 domain: null description: Volumetric water content at -5 kPa. - name: theta_10 + quantity: volumetric_water_content symbol: theta_10 - unit: cm^3/cm^3 + unit: volume_fraction + reported_unit: cm^3/cm^3 domain: null description: Volumetric water content at -10 kPa. - name: theta_30 + quantity: volumetric_water_content symbol: theta_30 - unit: cm^3/cm^3 + unit: volume_fraction + reported_unit: cm^3/cm^3 domain: null description: Volumetric water content at -30 kPa. - name: theta_60 + quantity: volumetric_water_content symbol: theta_60 - unit: cm^3/cm^3 + unit: volume_fraction + reported_unit: cm^3/cm^3 domain: null description: Volumetric water content at -60 kPa. - name: theta_100 + quantity: volumetric_water_content symbol: theta_100 - unit: cm^3/cm^3 + unit: volume_fraction + reported_unit: cm^3/cm^3 domain: null description: Volumetric water content at -100 kPa. - name: theta_500 + quantity: volumetric_water_content symbol: theta_500 - unit: cm^3/cm^3 + unit: volume_fraction + reported_unit: cm^3/cm^3 domain: null description: Volumetric water content at -500 kPa. - name: theta_1000 + quantity: volumetric_water_content symbol: theta_1000 - unit: cm^3/cm^3 + unit: volume_fraction + reported_unit: cm^3/cm^3 domain: null description: Volumetric water content at -1000 kPa. - name: theta_1500 + quantity: volumetric_water_content symbol: theta_1500 - unit: cm^3/cm^3 + unit: volume_fraction + reported_unit: cm^3/cm^3 domain: null description: Volumetric water content at -1500 kPa. - name: k_sat + quantity: saturated_hydraulic_conductivity symbol: Ks - unit: m/s + unit: meter_per_second + reported_unit: m/s domain: value >= 0 description: Saturated hydraulic conductivity. - golden_tests: + verification_cases: - id: cahaba_ap + kind: calculated inputs: sand: 70.9 fine_sand: 36.4 @@ -126,9 +149,7 @@ functions: theta_1000: 0.25187788 theta_1500: 0.22746346 k_sat: 4.2399741e-06 - rtol: 1.0e-08 - atol: 1.0e-12 - notes: Calculated from Table 4 using the Cahaba Ap inputs in Tables 2 and 3. + rationale: Calculated from Table 4 using the Cahaba Ap inputs in Tables 2 and 3. edge_cases: [] documentation: notes: diff --git a/specs/functions/rawls1982.yaml b/specs/functions/rawls1982.yaml index 7307569..6e4fa94 100644 --- a/specs/functions/rawls1982.yaml +++ b/specs/functions/rawls1982.yaml @@ -33,20 +33,21 @@ functions: outputs: type: scalar name: theta_1500 + quantity: volumetric_water_content symbol: theta_1500 - unit: cm^3/cm^3 + unit: volume_fraction + reported_unit: cm^3/cm^3 domain: value >= 0 description: Volumetric soil water content at -1500 kPa. - golden_tests: + verification_cases: - id: loamy_sand + kind: calculated inputs: clay: 5.12 organic_matter: 0.1 expected: theta_1500: 0.05318 - rtol: 1.0e-10 - atol: 1.0e-12 - notes: Calculated directly from the -15 bar first-level regression in Table 3. + rationale: Calculated directly from the -15 bar first-level regression in Table 3. edge_cases: [] documentation: notes: [] @@ -86,21 +87,22 @@ functions: outputs: type: scalar name: theta_33 + quantity: volumetric_water_content symbol: theta_33 - unit: cm^3/cm^3 + unit: volume_fraction + reported_unit: cm^3/cm^3 domain: value >= 0 description: Volumetric soil water content at -33 kPa. - golden_tests: + verification_cases: - id: loamy_sand_with_estimated_theta_1500 + kind: calculated inputs: sand: 85.0 organic_matter: 0.1 theta_1500: 0.05318 expected: theta_33: 0.1179896 - rtol: 1.0e-10 - atol: 1.0e-12 - notes: Calculated directly from the -0.33 bar second-level regression in Table + rationale: Calculated directly from the -0.33 bar second-level regression in Table 3. edge_cases: [] documentation: @@ -152,67 +154,92 @@ functions: name: Rawls1982PTFResult fields: - name: theta_4 + quantity: volumetric_water_content symbol: theta_4 - unit: cm^3/cm^3 + unit: volume_fraction + reported_unit: cm^3/cm^3 domain: null description: Volumetric water content at -4 kPa. - name: theta_7 + quantity: volumetric_water_content symbol: theta_7 - unit: cm^3/cm^3 + unit: volume_fraction + reported_unit: cm^3/cm^3 domain: null description: Volumetric water content at -7 kPa. - name: theta_10 + quantity: volumetric_water_content symbol: theta_10 - unit: cm^3/cm^3 + unit: volume_fraction + reported_unit: cm^3/cm^3 domain: null description: Volumetric water content at -10 kPa. - name: theta_20 + quantity: volumetric_water_content symbol: theta_20 - unit: cm^3/cm^3 + unit: volume_fraction + reported_unit: cm^3/cm^3 domain: null description: Volumetric water content at -20 kPa. - name: theta_33 + quantity: volumetric_water_content symbol: theta_33 - unit: cm^3/cm^3 + unit: volume_fraction + reported_unit: cm^3/cm^3 domain: null description: Input volumetric water content at -33 kPa. - name: theta_60 + quantity: volumetric_water_content symbol: theta_60 - unit: cm^3/cm^3 + unit: volume_fraction + reported_unit: cm^3/cm^3 domain: null description: Volumetric water content at -60 kPa. - name: theta_100 + quantity: volumetric_water_content symbol: theta_100 - unit: cm^3/cm^3 + unit: volume_fraction + reported_unit: cm^3/cm^3 domain: null description: Volumetric water content at -100 kPa. - name: theta_200 + quantity: volumetric_water_content symbol: theta_200 - unit: cm^3/cm^3 + unit: volume_fraction + reported_unit: cm^3/cm^3 domain: null description: Volumetric water content at -200 kPa. - name: theta_400 + quantity: volumetric_water_content symbol: theta_400 - unit: cm^3/cm^3 + unit: volume_fraction + reported_unit: cm^3/cm^3 domain: null description: Volumetric water content at -400 kPa. - name: theta_700 + quantity: volumetric_water_content symbol: theta_700 - unit: cm^3/cm^3 + unit: volume_fraction + reported_unit: cm^3/cm^3 domain: null description: Volumetric water content at -700 kPa. - name: theta_1000 + quantity: volumetric_water_content symbol: theta_1000 - unit: cm^3/cm^3 + unit: volume_fraction + reported_unit: cm^3/cm^3 domain: null description: Volumetric water content at -1000 kPa. - name: theta_1500 + quantity: volumetric_water_content symbol: theta_1500 - unit: cm^3/cm^3 + unit: volume_fraction + reported_unit: cm^3/cm^3 domain: null description: Input volumetric water content at -1500 kPa. - golden_tests: + verification_cases: - id: loamy_sand + kind: calculated inputs: sand: 85.0 organic_matter: 0.66 @@ -232,9 +259,7 @@ functions: theta_700: 0.041824 theta_1000: 0.038932 theta_1500: 0.033 - rtol: 1.0e-10 - atol: 1.0e-12 - notes: Calculated from the third-level regressions in Table 3 using the given + rationale: Calculated from the third-level regressions in Table 3 using the given endpoint water contents. edge_cases: [] documentation: diff --git a/specs/functions/saxton2006.yaml b/specs/functions/saxton2006.yaml index 6e10cfe..03af44d 100644 --- a/specs/functions/saxton2006.yaml +++ b/specs/functions/saxton2006.yaml @@ -50,57 +50,78 @@ functions: name: Saxton2006PTFResult fields: - name: theta_1500 + quantity: volumetric_water_content symbol: theta_1500 - unit: m^3/m^3 + unit: volume_fraction + reported_unit: m^3/m^3 domain: value > 0 description: Volumetric water content at 1500 kPa matric tension. - name: theta_33 + quantity: volumetric_water_content symbol: theta_33 - unit: m^3/m^3 + unit: volume_fraction + reported_unit: m^3/m^3 domain: value > 0 description: Volumetric water content at 33 kPa matric tension. - name: theta_s + quantity: volumetric_water_content symbol: theta_s - unit: m^3/m^3 + unit: volume_fraction + reported_unit: m^3/m^3 domain: value > 0 description: Saturated volumetric water content at normal density. - name: plant_available_water + quantity: volumetric_water_content symbol: PAW - unit: m^3/m^3 + unit: volume_fraction + reported_unit: m^3/m^3 domain: value >= 0 description: Difference between the 33 and 1500 kPa water contents. - name: air_entry_tension + quantity: matric_potential symbol: psi_e - unit: kPa + unit: kilopascal + reported_unit: kPa domain: value > 0 description: Air-entry or bubbling-pressure tension. - name: retention_a + quantity: retention_coefficient_a symbol: A - unit: kPa + unit: kilopascal + reported_unit: kPa domain: value > 0 description: Coefficient A of the dry-range moisture-tension curve. - name: retention_b + quantity: retention_coefficient_b symbol: B unit: dimensionless + reported_unit: dimensionless domain: value > 0 description: Exponent B of the dry-range moisture-tension curve. - name: conductivity_lambda + quantity: conductivity_shape_parameter symbol: lambda unit: dimensionless + reported_unit: dimensionless domain: value > 0 description: Inverse of retention exponent B. - name: saturated_conductivity + quantity: saturated_hydraulic_conductivity symbol: K_s - unit: mm/h + unit: millimeter_per_hour + reported_unit: mm/h domain: value >= 0 description: Saturated hydraulic conductivity of the matric soil. - name: normal_density + quantity: dry_bulk_density symbol: rho_N - unit: g/cm^3 + unit: gram_per_cubic_centimeter + reported_unit: g/cm^3 domain: value > 0 description: Normal dry bulk density assuming particle density 2.65 g/cm^3. - golden_tests: + verification_cases: - id: table_3_sand + kind: calculated inputs: sand: 0.88 clay: 0.05 @@ -116,9 +137,7 @@ functions: conductivity_lambda: 0.18776158355467926 saturated_conductivity: 108.1478278507403 normal_density: 1.4264356197312436 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: >- + rationale: >- Calculated directly from Table 1. The source-native rounded Table 3 values are 0.05, 0.10, 0.46, 0.05, 108.1 mm/h, and 1.43 g/cm^3 for theta_1500, theta_33, theta_s, PAW, K_s, and rho_N, respectively. @@ -207,27 +226,36 @@ functions: name: Saxton2006DensityResult fields: - name: adjusted_density + quantity: dry_bulk_density symbol: rho_DF - unit: g/cm^3 + unit: gram_per_cubic_centimeter + reported_unit: g/cm^3 domain: value > 0 description: Density after applying the density factor. - name: adjusted_theta_s + quantity: volumetric_water_content symbol: theta_S-DF - unit: m^3/m^3 + unit: volume_fraction + reported_unit: m^3/m^3 domain: value > 0 description: Saturated water content after the density adjustment. - name: adjusted_theta_33 + quantity: volumetric_water_content symbol: theta_33-DF - unit: m^3/m^3 + unit: volume_fraction + reported_unit: m^3/m^3 domain: value > 0 description: Water content at 33 kPa after the density adjustment. - name: adjusted_theta_s_minus_33 + quantity: volumetric_water_content symbol: theta_(S-33)DF - unit: m^3/m^3 + unit: volume_fraction + reported_unit: m^3/m^3 domain: value >= 0.005 description: Density-adjusted difference between saturation and 33 kPa. - golden_tests: + verification_cases: - id: table_3_sand_compacted + kind: calculated inputs: normal_density: 1.4264356197312436 theta_s: 0.46172240764858724 @@ -238,9 +266,7 @@ functions: adjusted_theta_s: 0.407894648413446 adjusted_theta_33: 0.09206237180155877 adjusted_theta_s_minus_33: 0.3158322766118873 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated directly from Table 1, Equations 7-10. + rationale: Calculated directly from Table 1, Equations 7-10. edge_cases: [] documentation: notes: @@ -288,21 +314,22 @@ functions: outputs: type: scalar name: tension + quantity: matric_potential symbol: psi_theta - unit: kPa + unit: kilopascal + reported_unit: kPa domain: 33 <= value <= 1500 description: Matric tension at the supplied water content. - golden_tests: + verification_cases: - id: table_3_sand_midpoint + kind: calculated inputs: theta: 0.07652425182429351 theta_1500: 0.05022058 theta_33: 0.10282792364858702 expected: tension: 159.18094591362183 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated directly from Table 1, Equations 11, 14, and 15. + rationale: Calculated directly from Table 1, Equations 11, 14, and 15. edge_cases: [] documentation: notes: [] @@ -356,12 +383,15 @@ functions: outputs: type: scalar name: tension + quantity: matric_potential symbol: psi_theta - unit: kPa + unit: kilopascal + reported_unit: kPa domain: air_entry_tension <= value <= 33 description: Matric tension at the supplied water content. - golden_tests: + verification_cases: - id: table_3_sand_theta_0_2 + kind: calculated inputs: theta: 0.2 theta_33: 0.10282792364858702 @@ -369,9 +399,7 @@ functions: air_entry_tension: 0.5986789729513293 expected: tension: 24.227216407352152 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated directly from Table 1, Equation 12. + rationale: Calculated directly from Table 1, Equation 12. edge_cases: [] documentation: notes: @@ -418,12 +446,15 @@ functions: outputs: type: scalar name: conductivity + quantity: unsaturated_hydraulic_conductivity symbol: K_theta - unit: mm/h + unit: millimeter_per_hour + reported_unit: mm/h domain: value >= 0 description: Unsaturated hydraulic conductivity at the supplied water content. - golden_tests: + verification_cases: - id: table_3_sand_theta_0_3 + kind: calculated inputs: theta: 0.3 theta_s: 0.46172240764858724 @@ -431,9 +462,7 @@ functions: conductivity_lambda: 0.18776158355467926 expected: conductivity: 0.3003203142764693 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated directly from corrected Table 1, Equation 17. + rationale: Calculated directly from corrected Table 1, Equation 17. edge_cases: [] documentation: notes: [] @@ -481,27 +510,36 @@ functions: name: Saxton2006GravelResult fields: - name: gravel_volume_fraction + quantity: gravel_volume_fraction symbol: R_v - unit: m^3/m^3 + unit: volume_fraction + reported_unit: m^3/m^3 domain: 0 <= value < 1 description: Gravel volume fraction of the bulk soil. - name: bulk_density + quantity: dry_bulk_density symbol: rho_B - unit: g/cm^3 + unit: gram_per_cubic_centimeter + reported_unit: g/cm^3 domain: value > 0 description: Dry bulk density of matric soil plus gravel. - name: bulk_plant_available_water + quantity: volumetric_water_content symbol: PAW_B - unit: m^3/m^3 + unit: volume_fraction + reported_unit: m^3/m^3 domain: value >= 0 description: Plant-available water on a bulk-soil volume basis. - name: bulk_saturated_conductivity + quantity: saturated_hydraulic_conductivity symbol: K_b - unit: mm/h + unit: millimeter_per_hour + reported_unit: mm/h domain: value >= 0 description: Saturated conductivity after the gravel reduction. - golden_tests: + verification_cases: - id: table_3_sand_twenty_percent_gravel + kind: calculated inputs: gravel_weight_fraction: 0.2 matric_density: 1.4264356197312436 @@ -512,9 +550,7 @@ functions: bulk_density: 1.5715605653291098 bulk_plant_available_water: 0.04636767370708995 bulk_saturated_conductivity: 97.54880510583706 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated directly from Table 1, Equations 19-22. + rationale: Calculated directly from Table 1, Equations 19-22. edge_cases: [] documentation: notes: @@ -567,17 +603,22 @@ functions: name: Saxton2006SalinityResult fields: - name: saturated_osmotic_potential + quantity: osmotic_potential symbol: psi_O - unit: kPa + unit: kilopascal + reported_unit: kPa domain: value >= 0 description: Osmotic potential at saturation. - name: osmotic_potential + quantity: osmotic_potential symbol: psi_Otheta - unit: kPa + unit: kilopascal + reported_unit: kPa domain: value >= 0 description: Osmotic potential at the supplied water content. - golden_tests: + verification_cases: - id: saline_partly_saturated_soil + kind: calculated inputs: electrical_conductivity: 4.0 theta: 0.3 @@ -585,9 +626,7 @@ functions: expected: saturated_osmotic_potential: 144.0 osmotic_potential: 221.6267556713219 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated directly from Table 1, Equations 23 and 24. + rationale: Calculated directly from Table 1, Equations 23 and 24. edge_cases: [] documentation: notes: diff --git a/specs/functions/tiwary2014.yaml b/specs/functions/tiwary2014.yaml index 4d704f5..b9ae867 100644 --- a/specs/functions/tiwary2014.yaml +++ b/specs/functions/tiwary2014.yaml @@ -40,21 +40,22 @@ functions: outputs: type: scalar name: k_sat + quantity: saturated_hydraulic_conductivity symbol: sHC - unit: m/s + unit: meter_per_second + reported_unit: m/s domain: null description: Saturated hydraulic conductivity converted from mm/h. - golden_tests: + verification_cases: - id: igp_saturated_conductivity_case + kind: calculated inputs: sand: 37.3 bulk_density: 1.674 esp: 4.6 expected: k_sat: 5.103578e-07 - rtol: 1.0e-10 - atol: 1.0e-12 - notes: Calculated directly from equation 11. + rationale: Calculated directly from equation 11. edge_cases: [] documentation: notes: @@ -117,27 +118,36 @@ functions: name: Tiwary2014PTFResult fields: - name: w_33 + quantity: gravimetric_water_content symbol: w33 - unit: '%' + unit: mass_percent + reported_unit: '%' domain: null description: Gravimetric water content at 33 kPa. - name: w_100 + quantity: gravimetric_water_content symbol: w100 - unit: '%' + unit: mass_percent + reported_unit: '%' domain: null description: Gravimetric water content at 100 kPa. - name: w_1500 + quantity: gravimetric_water_content symbol: w1500 - unit: '%' + unit: mass_percent + reported_unit: '%' domain: null description: Gravimetric water content at 1500 kPa. - name: k_sat + quantity: saturated_hydraulic_conductivity symbol: sHC - unit: m/s + unit: meter_per_second + reported_unit: m/s domain: null description: Saturated hydraulic conductivity converted from mm/h. - golden_tests: + verification_cases: - id: black_soil_compatibility_case + kind: calculated inputs: clay: 54.9 ph: 7.6 @@ -150,9 +160,7 @@ functions: w_100: 36.8273 w_1500: 21.6976 k_sat: 5.373367e-06 - rtol: 1.0e-10 - atol: 1.0e-12 - notes: Calculated directly from equations 7-10 for BSR soils. + rationale: Calculated directly from equations 7-10 for BSR soils. edge_cases: [] documentation: notes: diff --git a/specs/functions/varallyai1982.yaml b/specs/functions/varallyai1982.yaml index 3866955..0dd29df 100644 --- a/specs/functions/varallyai1982.yaml +++ b/specs/functions/varallyai1982.yaml @@ -35,18 +35,24 @@ $defs: name: Varallyai1982Parameters fields: - name: theta_0 + quantity: volumetric_water_content symbol: theta_0 - unit: vol.% + unit: volume_percent + reported_unit: vol.% domain: null description: Upper-asymptote water content parameter of equation (9). - name: m + quantity: varallyai_m symbol: m unit: dimensionless + reported_unit: dimensionless domain: null description: Shape parameter of equation (9). - name: pf_star + quantity: varallyai_pf_star symbol: pF_* unit: dimensionless + reported_unit: dimensionless domain: null description: >- Base-10 logarithm of the equation (9) suction-scale parameter psi_*, @@ -70,8 +76,9 @@ functions: - $ref: '#/$defs/fineFraction' outputs: $ref: '#/$defs/Varallyai1982Parameters' - golden_tests: + verification_cases: - id: representative_review_case + kind: calculated inputs: bulk_density: 1.4 fine_sand_fraction: 0.3 @@ -80,9 +87,7 @@ functions: theta_0: 46.54495 m: 0.10029 pf_star: 3.62445 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated directly from equation (14) during specification review. + rationale: Calculated directly from equation (14) during specification review. edge_cases: - id: nan_fine_fraction inputs: @@ -124,8 +129,9 @@ functions: - $ref: '#/$defs/fineFraction' outputs: $ref: '#/$defs/Varallyai1982Parameters' - golden_tests: + verification_cases: - id: representative_review_case + kind: calculated inputs: bulk_density: 1.4 fine_fraction: 0.35 @@ -133,9 +139,7 @@ functions: theta_0: 52.005 m: 0.4174 pf_star: 4.00469 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated directly from equation (15) during specification review. + rationale: Calculated directly from equation (15) during specification review. edge_cases: - id: nan_bulk_density inputs: @@ -175,8 +179,9 @@ functions: - $ref: '#/$defs/fineFraction' outputs: $ref: '#/$defs/Varallyai1982Parameters' - golden_tests: + verification_cases: - id: representative_review_case + kind: calculated inputs: bulk_density: 1.4 fine_fraction: 0.35 @@ -184,9 +189,7 @@ functions: theta_0: 47.60035 m: 0.407 pf_star: 3.9299 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated directly from equation (16) during specification review. + rationale: Calculated directly from equation (16) during specification review. edge_cases: - id: nan_bulk_density inputs: @@ -226,8 +229,9 @@ functions: - $ref: '#/$defs/fineFraction' outputs: $ref: '#/$defs/Varallyai1982Parameters' - golden_tests: + verification_cases: - id: representative_review_case + kind: calculated inputs: bulk_density: 1.4 fine_fraction: 0.35 @@ -235,9 +239,7 @@ functions: theta_0: 49.87 m: 0.84011 pf_star: 3.59772 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated directly from equation (17) during specification review. + rationale: Calculated directly from equation (17) during specification review. edge_cases: - id: nan_bulk_density inputs: @@ -286,5 +288,5 @@ scientific_notes: | - The source reports no exact calibration ranges. Applicability is therefore limited by documentation to comparable Hungarian meadow-series soils or the specified chernozem genetic horizon; no narrower numeric bounds are inferred. - - Golden values are review-derived evaluations of equations (14)-(17), not + - Verification values are review-derived evaluations of equations (14)-(17), not independently reported observations from the paper. diff --git a/specs/functions/vereecken1989.yaml b/specs/functions/vereecken1989.yaml index c459151..fc42f0f 100644 --- a/specs/functions/vereecken1989.yaml +++ b/specs/functions/vereecken1989.yaml @@ -57,27 +57,36 @@ functions: name: Vereecken1989PTFResult fields: - name: theta_r + quantity: volumetric_water_content symbol: theta_r - unit: cm^3/cm^3 + unit: volume_fraction + reported_unit: cm^3/cm^3 domain: 0 <= value <= 1 description: Residual volumetric soil water content. - name: theta_s + quantity: volumetric_water_content symbol: theta_s - unit: cm^3/cm^3 + unit: volume_fraction + reported_unit: cm^3/cm^3 domain: 0 <= value <= 1 description: Saturated volumetric soil water content. - name: alpha + quantity: van_genuchten_alpha symbol: alpha - unit: cm^-1 + unit: inverse_centimeter + reported_unit: cm^-1 domain: value > 0 description: Shape parameter of the van Genuchten moisture-retention model. - name: n + quantity: van_genuchten_n symbol: n unit: dimensionless + reported_unit: dimensionless domain: value > 0 description: Shape parameter of the van Genuchten moisture-retention model. - golden_tests: + verification_cases: - id: dataset_mean_properties + kind: calculated inputs: sand: 52.14 clay: 10.93 @@ -88,9 +97,7 @@ functions: theta_s: 0.4060520000000001 alpha: 0.003581613076579849 n: 0.8602234826041976 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: >- + rationale: >- Calculated from the Table 7 regressions using the dataset means reported in Tables 1 and 6 and the reviewed natural-log back-transformation. edge_cases: @@ -221,27 +228,36 @@ functions: name: Vereecken1989DetailedPTFResult fields: - name: theta_r + quantity: volumetric_water_content symbol: theta_r - unit: cm^3/cm^3 + unit: volume_fraction + reported_unit: cm^3/cm^3 domain: 0 <= value <= 1 description: Residual volumetric soil water content. - name: theta_s + quantity: volumetric_water_content symbol: theta_s - unit: cm^3/cm^3 + unit: volume_fraction + reported_unit: cm^3/cm^3 domain: 0 <= value <= 1 description: Saturated volumetric soil water content. - name: alpha + quantity: van_genuchten_alpha symbol: alpha - unit: cm^-1 + unit: inverse_centimeter + reported_unit: cm^-1 domain: value > 0 description: Shape parameter of the van Genuchten moisture-retention model. - name: n + quantity: van_genuchten_n symbol: n unit: dimensionless + reported_unit: dimensionless domain: value > 0 description: Shape parameter of the van Genuchten moisture-retention model. - golden_tests: + verification_cases: - id: dataset_mean_properties + kind: calculated inputs: particle_2000_1000: 0.25 particle_1000_500: 0.88 @@ -261,9 +277,7 @@ functions: theta_s: 0.4060520000000001 alpha: 0.02522115884641546 n: 0.517175605329397 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: >- + rationale: >- Calculated from the reviewed detailed regressions using dataset means from Tables 1 and 6, including restored carbon and bulk-density terms. edge_cases: @@ -379,7 +393,7 @@ scientific_notes: | infinity and out-of-domain numeric inputs. Applicability domains are documented but are not runtime clamps. - ## Golden-value policy + ## Verification-value policy - Golden values are calculated without rounding from the retained equations using + Verification values are calculated without rounding from the retained equations using the dataset means reported in Tables 1 and 6. diff --git a/specs/functions/wang2012.yaml b/specs/functions/wang2012.yaml index 2e6e2e6..b0d2fd3 100644 --- a/specs/functions/wang2012.yaml +++ b/specs/functions/wang2012.yaml @@ -58,24 +58,31 @@ functions: name: Wang2012PTFResult fields: - name: theta_s + quantity: volumetric_water_content symbol: SSWC - unit: cm^3/cm^3 + unit: volume_fraction + reported_unit: cm^3/cm^3 domain: value >= 0 description: Saturated volumetric water content normalized from the regression's volume-percent scale. - name: theta_fc + quantity: volumetric_water_content symbol: FC - unit: cm^3/cm^3 + unit: volume_fraction + reported_unit: cm^3/cm^3 domain: value >= 0 description: Volumetric water content at -33 kPa normalized from the regression's volume-percent scale. - name: k_sat + quantity: saturated_hydraulic_conductivity symbol: Ks - unit: m/s + unit: meter_per_second + reported_unit: m/s domain: value >= 0 description: Saturated hydraulic conductivity converted from cm/day. - golden_tests: + verification_cases: - id: derivation_minimum_soc_case + kind: calculated inputs: sand: 85.0 silt: 10.0 @@ -87,9 +94,7 @@ functions: theta_s: 0.61540575 theta_fc: 0.38491949 k_sat: 3.872974e-05 - rtol: 1.0e-06 - atol: 1.0e-12 - notes: Calculated from Table 6, converting SOC percent to g/kg and water-content + rationale: Calculated from Table 6, converting SOC percent to g/kg and water-content percentage points to volumetric fractions. edge_cases: - id: non_positive_log_or_reciprocal_input diff --git a/specs/functions/weber2020.yaml b/specs/functions/weber2020.yaml index b27e0b8..82906a2 100644 --- a/specs/functions/weber2020.yaml +++ b/specs/functions/weber2020.yaml @@ -62,42 +62,57 @@ functions: name: Weber2020PTFResult fields: - name: theta_snc_bw + quantity: volumetric_water_content symbol: theta_snc,BW - unit: dimensionless + unit: volume_fraction + reported_unit: dimensionless domain: null description: Saturated water content of the Brunswick noncapillary pore space. - name: theta_sc_bw + quantity: volumetric_water_content symbol: theta_sc,BW - unit: dimensionless + unit: volume_fraction + reported_unit: dimensionless domain: null description: Saturated water content of the Brunswick capillary pore space. - name: alpha_bw + quantity: van_genuchten_alpha symbol: alpha_BW - unit: cm^-1 + unit: inverse_centimeter + reported_unit: cm^-1 domain: null description: Brunswick-VGM inverse pressure-head scale parameter. - name: n_bw + quantity: brunswick_n symbol: n_BW unit: dimensionless + reported_unit: dimensionless domain: null description: Brunswick-VGM pore-size distribution shape parameter. - name: tau_bw + quantity: brunswick_tau symbol: tau_BW unit: dimensionless + reported_unit: dimensionless domain: null description: Brunswick capillary hydraulic-conductivity shape parameter. - name: k_sc_bw + quantity: saturated_hydraulic_conductivity symbol: K_sc,BW - unit: cm d^-1 + unit: centimeter_per_day + reported_unit: cm d^-1 domain: null description: Saturated capillary hydraulic conductivity. - name: k_snc_bw + quantity: saturated_hydraulic_conductivity symbol: K_snc,BW - unit: cm d^-1 + unit: centimeter_per_day + reported_unit: cm d^-1 domain: null description: Saturated noncapillary hydraulic conductivity. - golden_tests: + verification_cases: - id: representative_vgm_parameters + kind: calculated inputs: theta_r_vgm: 0.05 theta_s_vgm: 0.45 @@ -113,9 +128,7 @@ functions: tau_bw: -0.887 k_sc_bw: 172.186857498601 k_snc_bw: 0.0190546071796325 - rtol: 1.0e-12 - atol: 1.0e-12 - notes: Calculated directly from the Table 3 regressions using the confirmed constrained-tau + rationale: Calculated directly from the Table 3 regressions using the confirmed constrained-tau interpretation. edge_cases: - id: positive_tau_constraint @@ -179,7 +192,7 @@ scientific_notes: | The logarithmic regressions require positive `alpha_vgm`, `k_s_vgm`, and `n_vgm - 1`. - ## Golden-test rationale + ## Verification-test rationale The representative case uses values within the Table 1 parameter bounds. Its seven expected outputs are calculated directly from the Table 3 coefficients, diff --git a/specs/quantities.yaml b/specs/quantities.yaml new file mode 100644 index 0000000..3a40885 --- /dev/null +++ b/specs/quantities.yaml @@ -0,0 +1,206 @@ +volumetric_water_content: + description: Volumetric soil water content or an equivalent volumetric fraction. + units: + volume_fraction: + absolute: 0.001 + rationale: Project-default meaningful resolution for a volumetric fraction. + volume_percent: + absolute: 0.1 + rationale: Project-default meaningful resolution in volume percentage points. + +gravimetric_water_content: + description: Gravimetric soil water content or a difference between such contents. + units: + mass_fraction: + absolute: 0.001 + rationale: Project-default meaningful resolution for a gravimetric fraction. + mass_percent: + absolute: 0.1 + rationale: Project-default meaningful resolution in mass percentage points. + +available_water_capacity: + description: Available water expressed as an equivalent depth per soil depth. + units: + millimeter_per_meter: + absolute: 0.1 + rationale: Project-default meaningful resolution for water-storage depth. + +saturated_hydraulic_conductivity: + description: Saturated hydraulic conductivity, including pore-domain components. + units: + meter_per_second: + absolute: 1.0e-10 + relative: 0.01 + rationale: Conductivity spans orders of magnitude; use one percent with a lower resolution. + centimeter_per_hour: + absolute: 1.0e-5 + relative: 0.01 + rationale: Conductivity spans orders of magnitude; use one percent with a lower resolution. + millimeter_per_hour: + absolute: 1.0e-4 + relative: 0.01 + rationale: Conductivity spans orders of magnitude; use one percent with a lower resolution. + centimeter_per_minute: + absolute: 1.0e-5 + relative: 0.01 + rationale: Conductivity spans orders of magnitude; use one percent with a lower resolution. + centimeter_per_day: + absolute: 1.0e-4 + relative: 0.01 + rationale: Conductivity spans orders of magnitude; use one percent with a lower resolution. + +unsaturated_hydraulic_conductivity: + description: Hydraulic conductivity below saturation. + units: + millimeter_per_hour: + absolute: 1.0e-4 + relative: 0.01 + rationale: Conductivity spans orders of magnitude; use one percent with a lower resolution. + +infiltration_rate: + description: Soil infiltration rate. + units: + millimeter_per_hour: + absolute: 1.0e-4 + relative: 0.01 + rationale: Infiltration rate spans a broad range; use one percent with a lower resolution. + +dry_bulk_density: + description: Dry bulk density, including density adjusted for gravel or compaction. + units: + gram_per_cubic_centimeter: + absolute: 0.001 + rationale: Project-default meaningful resolution for dry bulk density. + +matric_potential: + description: Soil matric potential, tension, suction, or pressure head. + units: + kilopascal: + absolute: 0.01 + rationale: Project-default meaningful resolution for pressure or tension. + centimeter_water_head: + absolute: 0.01 + rationale: Project-default meaningful resolution for pressure head. + +osmotic_potential: + description: Soil-water osmotic potential. + units: + kilopascal: + absolute: 0.01 + rationale: Project-default meaningful resolution for osmotic potential. + +van_genuchten_alpha: + description: Inverse pressure-scale parameter of a van Genuchten retention model. + units: + inverse_centimeter: + absolute: 1.0e-5 + rationale: Project-default meaningful resolution for an inverse-length parameter. + inverse_kilopascal: + absolute: 1.0e-5 + rationale: Project-default meaningful resolution for an inverse-pressure parameter. + +van_genuchten_n: + description: Dimensionless van Genuchten pore-size distribution parameter. + units: + dimensionless: + absolute: 0.001 + rationale: Project-default meaningful resolution for a fitted shape parameter. + +campbell_b: + description: Campbell moisture-characteristic exponent. + units: + dimensionless: + absolute: 0.001 + rationale: Project-default meaningful resolution for a fitted shape parameter. + +sorptivity: + description: Soil-water sorptivity. + units: + centimeter_per_square_root_minute: + absolute: 1.0e-4 + relative: 0.01 + rationale: Sorptivity varies in scale; use one percent with a lower resolution. + +hutson_cass_b: + description: Dimensionless Hutson-Cass retention-curve shape parameter. + units: + dimensionless: + absolute: 0.001 + rationale: Project-default meaningful resolution for a fitted shape parameter. + +varallyai_m: + description: Shape parameter of the Varallyay retention equation. + units: + dimensionless: + absolute: 0.001 + rationale: Project-default meaningful resolution for a fitted shape parameter. + +varallyai_pf_star: + description: Logarithmic suction-scale parameter of the Varallyay retention equation. + units: + dimensionless: + absolute: 0.001 + rationale: Project-default meaningful resolution for a logarithmic fitted parameter. + +retention_coefficient_a: + description: Pressure-dimensional coefficient of an empirical retention relation. + units: + kilopascal: + absolute: 1.0e-9 + relative: 0.01 + rationale: >- + Project-default tolerance of one percent for the multiplicative + retention coefficient. At fixed exponent B and water content, + a relative change in A produces the same relative change in + predicted tension. The absolute tolerance is retained as a + provisional numerical floor near zero, not as source-reported + accuracy or measurement resolution. + +retention_coefficient_b: + description: Dimensionless exponent of an empirical retention relation. + units: + dimensionless: + absolute: 0.001 + rationale: Project-default meaningful resolution for a fitted shape parameter. + +conductivity_shape_parameter: + description: Dimensionless hydraulic-conductivity shape parameter. + units: + dimensionless: + absolute: 0.001 + rationale: Project-default meaningful resolution for a fitted shape parameter. + +log_saturated_hydraulic_conductivity: + description: Reported logarithm of saturated hydraulic conductivity. + units: + reported_log_value: + absolute: 0.001 + rationale: Project-default meaningful resolution for a reported logarithmic value. + +log_saturation_potential: + description: Reported logarithm of saturation matric potential. + units: + reported_log_value: + absolute: 0.001 + rationale: Project-default meaningful resolution for a reported logarithmic value. + +gravel_volume_fraction: + description: Volumetric fraction of gravel in bulk soil. + units: + volume_fraction: + absolute: 0.001 + rationale: Project-default meaningful resolution for a volumetric fraction. + +brunswick_n: + description: Brunswick-VGM pore-size distribution shape parameter. + units: + dimensionless: + absolute: 0.001 + rationale: Project-default meaningful resolution for a fitted shape parameter. + +brunswick_tau: + description: Brunswick hydraulic-conductivity shape parameter. + units: + dimensionless: + absolute: 0.001 + rationale: Project-default meaningful resolution for a fitted shape parameter. diff --git a/specs/schema/ptf-spec.schema.json b/specs/schema/ptf-spec.schema.json index 3ba4f40..eab4aae 100644 --- a/specs/schema/ptf-spec.schema.json +++ b/specs/schema/ptf-spec.schema.json @@ -171,6 +171,30 @@ "description": { "type": "string", "minLength": 1 } } }, + "outputField": { + "type": "object", + "additionalProperties": false, + "required": ["name", "quantity", "symbol", "unit", "reported_unit", "domain", "description"], + "properties": { + "name": { "type": "string", "minLength": 1 }, + "quantity": { "type": "string", "pattern": "^[a-z][a-z0-9_]*$" }, + "reported_unit": { "type": "string", "minLength": 1 }, + "symbol": { "$ref": "#/$defs/stringOrNull" }, + "unit": { "type": "string", "pattern": "^[a-z][a-z0-9_]*$" }, + "domain": { "$ref": "#/$defs/stringOrNull" }, + "description": { "type": "string", "minLength": 1 } + } + }, + "verificationToleranceOverride": { + "type": "object", + "additionalProperties": false, + "required": ["absolute", "source_location"], + "properties": { + "absolute": { "type": "number", "exclusiveMinimum": 0 }, + "relative": { "type": "number", "minimum": 0 }, + "source_location": { "type": "string", "minLength": 1 } + } + }, "inputReference": { "type": "object", "additionalProperties": false, @@ -228,16 +252,20 @@ "required": [ "type", "name", + "quantity", "symbol", "unit", + "reported_unit", "domain", "description" ], "properties": { "type": { "const": "scalar" }, "name": { "type": "string", "minLength": 1 }, + "quantity": { "type": "string", "pattern": "^[a-z][a-z0-9_]*$" }, + "reported_unit": { "type": "string", "minLength": 1 }, "symbol": { "$ref": "#/$defs/stringOrNull" }, - "unit": { "type": "string", "minLength": 1 }, + "unit": { "type": "string", "pattern": "^[a-z][a-z0-9_]*$" }, "domain": { "$ref": "#/$defs/stringOrNull" }, "description": { "type": "string", "minLength": 1 } } @@ -252,24 +280,42 @@ "fields": { "type": "array", "minItems": 1, - "items": { "$ref": "#/$defs/parameter" } + "items": { "$ref": "#/$defs/outputField" } } } } ] }, - "goldenTest": { - "type": "object", - "additionalProperties": false, - "required": ["id", "inputs", "expected", "rtol", "atol", "notes"], - "properties": { - "id": { "type": "string", "minLength": 1 }, - "inputs": { "type": "object", "minProperties": 1 }, - "expected": { "type": "object", "minProperties": 1 }, - "rtol": { "type": "number", "minimum": 0 }, - "atol": { "type": "number", "minimum": 0 }, - "notes": { "type": "string" } - } + "verificationCase": { + "oneOf": [ + { + "type": "object", + "additionalProperties": false, + "required": ["id", "kind", "inputs", "expected", "source_location"], + "properties": { + "id": { "type": "string", "minLength": 1 }, + "kind": { "const": "published" }, + "inputs": { "type": "object", "minProperties": 1 }, + "expected": { "type": "object", "minProperties": 1 }, + "source_location": { "type": "string", "minLength": 1 }, + "notes": { "type": "string" } + } + }, + { + "type": "object", + "additionalProperties": false, + "required": ["id", "kind", "inputs", "expected", "rationale"], + "properties": { + "id": { "type": "string", "minLength": 1 }, + "kind": { "const": "calculated" }, + "inputs": { "type": "object", "minProperties": 1 }, + "expected": { "type": "object", "minProperties": 1 }, + "source_location": { "type": "string", "minLength": 1 }, + "rationale": { "type": "string", "minLength": 1 }, + "notes": { "type": "string" } + } + } + ] }, "edgeCase": { "type": "object", @@ -300,7 +346,12 @@ "status": { "enum": ["ready-for-implementation", "implemented"] } } }, - "then": { "required": ["implementation"] } + "then": { + "required": ["implementation", "verification_cases"], + "properties": { + "verification_cases": { "minItems": 1 } + } + } } ], "properties": { @@ -344,9 +395,14 @@ { "$ref": "#/$defs/localReference" } ] }, - "golden_tests": { + "verification_tolerances": { + "type": "object", + "propertyNames": { "pattern": "^[a-z][a-z0-9_]*$" }, + "additionalProperties": { "$ref": "#/$defs/verificationToleranceOverride" } + }, + "verification_cases": { "type": "array", - "items": { "$ref": "#/$defs/goldenTest" } + "items": { "$ref": "#/$defs/verificationCase" } }, "edge_cases": { "type": "array", diff --git a/specs/units.yaml b/specs/units.yaml new file mode 100644 index 0000000..346ba0a --- /dev/null +++ b/specs/units.yaml @@ -0,0 +1,71 @@ +volume_fraction: + preferred_notation: "m^3/m^3" + aliases: ["cm^3/cm^3", "cm³/cm³", "m³/m³", "1", "dimensionless"] + +volume_percent: + preferred_notation: "% v/v" + aliases: ["vol.%", "% volume/volume", "%"] + +mass_fraction: + preferred_notation: "kg/kg" + aliases: ["g/g"] + +mass_percent: + preferred_notation: "% w/w" + aliases: ["%", "% mass"] + +millimeter_per_meter: + preferred_notation: "mm/m" + aliases: [] + +meter_per_second: + preferred_notation: "m/s" + aliases: [] + +centimeter_per_hour: + preferred_notation: "cm/h" + aliases: [] + +millimeter_per_hour: + preferred_notation: "mm/h" + aliases: ["mm h^-1"] + +centimeter_per_minute: + preferred_notation: "cm/min" + aliases: [] + +centimeter_per_day: + preferred_notation: "cm d^-1" + aliases: ["cm/day"] + +gram_per_cubic_centimeter: + preferred_notation: "g/cm^3" + aliases: [] + +kilopascal: + preferred_notation: "kPa" + aliases: [] + +centimeter_water_head: + preferred_notation: "cm H2O" + aliases: ["cm"] + +inverse_centimeter: + preferred_notation: "cm^-1" + aliases: [] + +inverse_kilopascal: + preferred_notation: "kPa^-1" + aliases: [] + +dimensionless: + preferred_notation: "dimensionless" + aliases: ["1"] + +centimeter_per_square_root_minute: + preferred_notation: "cm/min^(1/2)" + aliases: [] + +reported_log_value: + preferred_notation: "reported log value" + aliases: [] diff --git a/targets/ptfkit-native/tests/c/ahuja1984.c b/targets/ptfkit-native/tests/c/ahuja1984.c index f18663d..489d792 100644 --- a/targets/ptfkit-native/tests/c/ahuja1984.c +++ b/targets/ptfkit-native/tests/c/ahuja1984.c @@ -1,16 +1,18 @@ /* @generated by ptfkit-codegen; DO NOT EDIT. */ #include -#include "close_enough.h" +#include "support/close_enough.h" int main() { { const double result = calc_ptf_ahuja1984(0.45, 0.25, 100.0, 4.0); - assert_close_enough(result, 0.16, 0.00000000000001, 0.000000000001); + assert_close(result, 0.16, 0.00001, 0.01, "saturated_hydraulic_conductivity", + "centimeter_per_hour", "registry", "exponent_four"); } { const double result = calc_ptf_ahuja1984(0.5, 0.2, 100.0, 5.0); - assert_close_enough(result, 0.243, 0.00000000000001, 0.000000000001); + assert_close(result, 0.243, 0.00001, 0.01, "saturated_hydraulic_conductivity", + "centimeter_per_hour", "registry", "exponent_five"); } return 0; } diff --git a/targets/ptfkit-native/tests/c/aimrun2009.c b/targets/ptfkit-native/tests/c/aimrun2009.c index 66cf585..5fbf313 100644 --- a/targets/ptfkit-native/tests/c/aimrun2009.c +++ b/targets/ptfkit-native/tests/c/aimrun2009.c @@ -1,24 +1,32 @@ /* @generated by ptfkit-codegen; DO NOT EDIT. */ #include -#include "close_enough.h" +#include "support/close_enough.h" int main() { { const double result = calc_ptf_aimrun2009(43.88, 0.94, 12.07, 0.01); - assert_close_enough(result, 0.00000007358406556179513, 0.000000000001, 0.00000001); + assert_close(result, 0.00000007358406556179513, 0.0000000001, 0.01, + "saturated_hydraulic_conductivity", "meter_per_second", "registry", + "mean_topsoil_layer"); } { const double result = calc_ptf_aimrun2009(50.21, 1.19, 8.55, 0.007); - assert_close_enough(result, 0.0000000307872446717209, 0.000000000001, 0.00000001); + assert_close(result, 0.0000000307872446717209, 0.0000000001, 0.01, + "saturated_hydraulic_conductivity", "meter_per_second", "registry", + "mean_hardpan_layer"); } { const double result = calc_ptf_aimrun2009(58.81, 1.13, 5.12, 0.005); - assert_close_enough(result, 0.000000023343051908963327, 0.000000000001, 0.00000001); + assert_close(result, 0.000000023343051908963327, 0.0000000001, 0.01, + "saturated_hydraulic_conductivity", "meter_per_second", "registry", + "mean_subsoil_layer"); } { const double result = calc_ptf_aimrun2009(47.5, 1.08, 1.43, 0.008); - assert_close_enough(result, 0.00000003831168764444974, 0.000000000001, 0.00000001); + assert_close(result, 0.00000003831168764444974, 0.0000000001, 0.01, + "saturated_hydraulic_conductivity", "meter_per_second", "registry", + "min_organic_matter"); } return 0; } diff --git a/targets/ptfkit-native/tests/c/beniaich2023.c b/targets/ptfkit-native/tests/c/beniaich2023.c index fae9c1b..a5f0195 100644 --- a/targets/ptfkit-native/tests/c/beniaich2023.c +++ b/targets/ptfkit-native/tests/c/beniaich2023.c @@ -1,74 +1,107 @@ /* @generated by ptfkit-codegen; DO NOT EDIT. */ #include -#include "close_enough.h" +#include "support/close_enough.h" int main() { { const beniaich2023_ptf_result result = calc_ptf_beniaich2023_slr1(20.0); - assert_close_enough(result.water_saturation, 0.57427, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_field_capacity, 0.17577, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_wilting_point, 0.09621, 0.000000000001, 0.000000000001); + assert_close(result.water_saturation, 0.57427, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_field_capacity, 0.17577, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_wilting_point, 0.09621, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); } { const beniaich2023_ptf_result result = calc_ptf_beniaich2023_slr2(30.0); - assert_close_enough(result.water_saturation, 0.68478, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_field_capacity, 0.24878, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_wilting_point, 0.16131, 0.000000000001, 0.000000000001); + assert_close(result.water_saturation, 0.68478, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_field_capacity, 0.24878, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_wilting_point, 0.16131, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); } { const beniaich2023_ptf_result result = calc_ptf_beniaich2023_slr3(50.0); - assert_close_enough(result.water_saturation, 0.6007, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_field_capacity, 0.1848, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_wilting_point, 0.11077, 0.000000000001, 0.000000000001); + assert_close(result.water_saturation, 0.6007, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_field_capacity, 0.1848, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_wilting_point, 0.11077, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); } { const beniaich2023_ptf_result result = calc_ptf_beniaich2023_slr4(20.0, 30.0); - assert_close_enough(result.water_saturation, 0.74501, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_field_capacity, 0.30678, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_wilting_point, 0.19915, 0.000000000001, 0.000000000001); + assert_close(result.water_saturation, 0.74501, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_field_capacity, 0.30678, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_wilting_point, 0.19915, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); } { const beniaich2023_ptf_result result = calc_ptf_beniaich2023_slr5(20.0, 40.0); - assert_close_enough(result.water_saturation, 0.68578, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_field_capacity, 0.241875, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_wilting_point, 0.16176, 0.000000000001, 0.000000000001); + assert_close(result.water_saturation, 0.68578, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_field_capacity, 0.241875, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_wilting_point, 0.16176, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); } { const beniaich2023_ptf_result result = calc_ptf_beniaich2023_slr6(2.0); - assert_close_enough(result.water_saturation, 0.66749, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_field_capacity, 0.24009, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_wilting_point, 0.15562, 0.000000000001, 0.000000000001); + assert_close(result.water_saturation, 0.66749, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_field_capacity, 0.24009, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_wilting_point, 0.15562, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); } { const beniaich2023_ptf_result result = calc_ptf_beniaich2023_mlr1(30.0, 50.0, 2.0); - assert_close_enough(result.water_saturation, 0.56266, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_field_capacity, 0.17238, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_wilting_point, 0.09366, 0.000000000001, 0.000000000001); + assert_close(result.water_saturation, 0.56266, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_field_capacity, 0.17238, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_wilting_point, 0.09366, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); } { const beniaich2023_ptf_result result = calc_ptf_beniaich2023_mlr2(50.0, 2.0); - assert_close_enough(result.water_saturation, 0.58954, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_field_capacity, 0.18025, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_wilting_point, 0.10825, 0.000000000001, 0.000000000001); + assert_close(result.water_saturation, 0.58954, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_field_capacity, 0.18025, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_wilting_point, 0.10825, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); } { const beniaich2023_ptf_result result = calc_ptf_beniaich2023_mlr3(30.0, 2.0); - assert_close_enough(result.water_saturation, 0.67031, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_field_capacity, 0.24275, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_wilting_point, 0.15755, 0.000000000001, 0.000000000001); + assert_close(result.water_saturation, 0.67031, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_field_capacity, 0.24275, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_wilting_point, 0.15755, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); } { const beniaich2023_ptf_result result = calc_ptf_beniaich2023_mlr4(20.0, 2.0); - assert_close_enough(result.water_saturation, 0.5589, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_field_capacity, 0.16859, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_wilting_point, 0.09157, 0.000000000001, 0.000000000001); + assert_close(result.water_saturation, 0.5589, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_field_capacity, 0.16859, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_wilting_point, 0.09157, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); } { const beniaich2023_ptf_result result = calc_ptf_beniaich2023_mlr5(20.0, 30.0, 2.0); - assert_close_enough(result.water_saturation, 0.56229, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_field_capacity, 0.172, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_wilting_point, 0.09379, 0.000000000001, 0.000000000001); + assert_close(result.water_saturation, 0.56229, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_field_capacity, 0.172, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_wilting_point, 0.09379, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); } return 0; } diff --git a/targets/ptfkit-native/tests/c/chakraborty2011.c b/targets/ptfkit-native/tests/c/chakraborty2011.c index 8f922dd..0f3bbc4 100644 --- a/targets/ptfkit-native/tests/c/chakraborty2011.c +++ b/targets/ptfkit-native/tests/c/chakraborty2011.c @@ -1,52 +1,76 @@ /* @generated by ptfkit-codegen; DO NOT EDIT. */ #include -#include "close_enough.h" +#include "support/close_enough.h" int main() { { const chakraborty2011_ptf_result result = calc_ptf_chakraborty2011_eq1(20.0, 30.0); - assert_close_enough(result.water_content_33, 0.2488, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_content_100, 0.19758, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_content_500, 0.13528, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_content_1500, 0.11702, 0.000000000001, 0.000000000001); + assert_close(result.water_content_33, 0.2488, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_content_100, 0.19758, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_content_500, 0.13528, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_content_1500, 0.11702, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); } { const chakraborty2011_ptf_result result = calc_ptf_chakraborty2011_eq2(50.0, 1.5); - assert_close_enough(result.water_content_33, 0.219415, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_content_100, 0.175765, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_content_500, 0.141025, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_content_1500, 0.118315, 0.000000000001, 0.000000000001); + assert_close(result.water_content_33, 0.219415, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_content_100, 0.175765, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_content_500, 0.141025, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_content_1500, 0.118315, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); } { const chakraborty2011_ptf_result result = calc_ptf_chakraborty2011_eq3(20.0, 30.0, 1.5); - assert_close_enough(result.water_content_33, 0.25776, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_content_100, 0.201085, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_content_500, 0.139175, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_content_1500, 0.117865, 0.000000000001, 0.000000000001); + assert_close(result.water_content_33, 0.25776, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_content_100, 0.201085, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_content_500, 0.139175, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_content_1500, 0.117865, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); } { const chakraborty2011_ptf_result result = calc_ptf_chakraborty2011_eq4(20.0, 30.0, 50.0); - assert_close_enough(result.water_content_33, 0.24397, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_content_100, 0.19235, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_content_500, 0.13116, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_content_1500, 0.11325, 0.000000000001, 0.000000000001); + assert_close(result.water_content_33, 0.24397, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_content_100, 0.19235, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_content_500, 0.13116, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_content_1500, 0.11325, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); } { const chakraborty2011_ptf_result result = calc_ptf_chakraborty2011_eq5(20.0, 30.0, 50.0, 1.5); - assert_close_enough(result.water_content_33, 0.249785, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_content_100, 0.19166, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_content_500, 0.131065, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_content_1500, 0.110595, 0.000000000001, 0.000000000001); + assert_close(result.water_content_33, 0.249785, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_content_100, 0.19166, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_content_500, 0.131065, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_content_1500, 0.110595, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); } { const chakraborty2011_ptf_result result = calc_ptf_chakraborty2011_eq6(20.0, 30.0, 50.0, 0.5, 1.5); - assert_close_enough(result.water_content_33, 0.25207, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_content_100, 0.19695, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_content_500, 0.13364, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_content_1500, 0.113175, 0.000000000001, 0.000000000001); + assert_close(result.water_content_33, 0.25207, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_content_100, 0.19695, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_content_500, 0.13364, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_content_1500, 0.113175, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); } return 0; } diff --git a/targets/ptfkit-native/tests/c/clapp1978.c b/targets/ptfkit-native/tests/c/clapp1978.c index 3b0291f..371793b 100644 --- a/targets/ptfkit-native/tests/c/clapp1978.c +++ b/targets/ptfkit-native/tests/c/clapp1978.c @@ -1,115 +1,192 @@ /* @generated by ptfkit-codegen; DO NOT EDIT. */ #include -#include "close_enough.h" +#include "support/close_enough.h" int main() { { const clapp1978_parameters result = calc_ptf_clapp1978(clapp1978_usda_texture_class_sand); - assert_close_enough(result.b, 4.05, 0.0, 0.0); - assert_close_enough(result.saturation_suction, 3.5, 0.0, 0.0); - assert_close_enough(result.wetting_front_suction, 4.66, 0.0, 0.0); - assert_close_enough(result.saturated_water_content, 0.395, 0.0, 0.0); - assert_close_enough(result.saturated_hydraulic_conductivity, 1.056, 0.0, 0.0); - assert_close_enough(result.sorptivity, 1.52, 0.0, 0.0); + assert_close(result.b, 4.05, 0.001, 0.0, "campbell_b", "dimensionless", "registry", + "table_2_sand"); + assert_close(result.saturation_suction, 3.5, 0.01, 0.0, "matric_potential", + "centimeter_water_head", "registry", "table_2_sand"); + assert_close(result.wetting_front_suction, 4.66, 0.01, 0.0, "matric_potential", + "centimeter_water_head", "registry", "table_2_sand"); + assert_close(result.saturated_water_content, 0.395, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "table_2_sand"); + assert_close(result.saturated_hydraulic_conductivity, 1.056, 0.00001, 0.01, + "saturated_hydraulic_conductivity", "centimeter_per_minute", "registry", + "table_2_sand"); + assert_close(result.sorptivity, 1.52, 0.0001, 0.01, "sorptivity", + "centimeter_per_square_root_minute", "registry", "table_2_sand"); } { const clapp1978_parameters result = calc_ptf_clapp1978(clapp1978_usda_texture_class_loamy_sand); - assert_close_enough(result.b, 4.38, 0.0, 0.0); - assert_close_enough(result.saturation_suction, 1.78, 0.0, 0.0); - assert_close_enough(result.wetting_front_suction, 2.38, 0.0, 0.0); - assert_close_enough(result.saturated_water_content, 0.41, 0.0, 0.0); - assert_close_enough(result.saturated_hydraulic_conductivity, 0.938, 0.0, 0.0); - assert_close_enough(result.sorptivity, 1.04, 0.0, 0.0); + assert_close(result.b, 4.38, 0.001, 0.0, "campbell_b", "dimensionless", "registry", + "table_2_loamy_sand"); + assert_close(result.saturation_suction, 1.78, 0.01, 0.0, "matric_potential", + "centimeter_water_head", "registry", "table_2_loamy_sand"); + assert_close(result.wetting_front_suction, 2.38, 0.01, 0.0, "matric_potential", + "centimeter_water_head", "registry", "table_2_loamy_sand"); + assert_close(result.saturated_water_content, 0.41, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "table_2_loamy_sand"); + assert_close(result.saturated_hydraulic_conductivity, 0.938, 0.00001, 0.01, + "saturated_hydraulic_conductivity", "centimeter_per_minute", "registry", + "table_2_loamy_sand"); + assert_close(result.sorptivity, 1.04, 0.0001, 0.01, "sorptivity", + "centimeter_per_square_root_minute", "registry", "table_2_loamy_sand"); } { const clapp1978_parameters result = calc_ptf_clapp1978(clapp1978_usda_texture_class_sandy_loam); - assert_close_enough(result.b, 4.9, 0.0, 0.0); - assert_close_enough(result.saturation_suction, 7.18, 0.0, 0.0); - assert_close_enough(result.wetting_front_suction, 9.52, 0.0, 0.0); - assert_close_enough(result.saturated_water_content, 0.435, 0.0, 0.0); - assert_close_enough(result.saturated_hydraulic_conductivity, 0.208, 0.0, 0.0); - assert_close_enough(result.sorptivity, 1.03, 0.0, 0.0); + assert_close(result.b, 4.9, 0.001, 0.0, "campbell_b", "dimensionless", "registry", + "table_2_sandy_loam"); + assert_close(result.saturation_suction, 7.18, 0.01, 0.0, "matric_potential", + "centimeter_water_head", "registry", "table_2_sandy_loam"); + assert_close(result.wetting_front_suction, 9.52, 0.01, 0.0, "matric_potential", + "centimeter_water_head", "registry", "table_2_sandy_loam"); + assert_close(result.saturated_water_content, 0.435, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "table_2_sandy_loam"); + assert_close(result.saturated_hydraulic_conductivity, 0.208, 0.00001, 0.01, + "saturated_hydraulic_conductivity", "centimeter_per_minute", "registry", + "table_2_sandy_loam"); + assert_close(result.sorptivity, 1.03, 0.0001, 0.01, "sorptivity", + "centimeter_per_square_root_minute", "registry", "table_2_sandy_loam"); } { const clapp1978_parameters result = calc_ptf_clapp1978(clapp1978_usda_texture_class_silt_loam); - assert_close_enough(result.b, 5.3, 0.0, 0.0); - assert_close_enough(result.saturation_suction, 56.6, 0.0, 0.0); - assert_close_enough(result.wetting_front_suction, 75.3, 0.0, 0.0); - assert_close_enough(result.saturated_water_content, 0.485, 0.0, 0.0); - assert_close_enough(result.saturated_hydraulic_conductivity, 0.0432, 0.0, 0.0); - assert_close_enough(result.sorptivity, 1.26, 0.0, 0.0); + assert_close(result.b, 5.3, 0.001, 0.0, "campbell_b", "dimensionless", "registry", + "table_2_silt_loam"); + assert_close(result.saturation_suction, 56.6, 0.01, 0.0, "matric_potential", + "centimeter_water_head", "registry", "table_2_silt_loam"); + assert_close(result.wetting_front_suction, 75.3, 0.01, 0.0, "matric_potential", + "centimeter_water_head", "registry", "table_2_silt_loam"); + assert_close(result.saturated_water_content, 0.485, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "table_2_silt_loam"); + assert_close(result.saturated_hydraulic_conductivity, 0.0432, 0.00001, 0.01, + "saturated_hydraulic_conductivity", "centimeter_per_minute", "registry", + "table_2_silt_loam"); + assert_close(result.sorptivity, 1.26, 0.0001, 0.01, "sorptivity", + "centimeter_per_square_root_minute", "registry", "table_2_silt_loam"); } { const clapp1978_parameters result = calc_ptf_clapp1978(clapp1978_usda_texture_class_loam); - assert_close_enough(result.b, 5.39, 0.0, 0.0); - assert_close_enough(result.saturation_suction, 14.6, 0.0, 0.0); - assert_close_enough(result.wetting_front_suction, 20.0, 0.0, 0.0); - assert_close_enough(result.saturated_water_content, 0.451, 0.0, 0.0); - assert_close_enough(result.saturated_hydraulic_conductivity, 0.0417, 0.0, 0.0); - assert_close_enough(result.sorptivity, 0.693, 0.0, 0.0); + assert_close(result.b, 5.39, 0.001, 0.0, "campbell_b", "dimensionless", "registry", + "table_2_loam"); + assert_close(result.saturation_suction, 14.6, 0.01, 0.0, "matric_potential", + "centimeter_water_head", "registry", "table_2_loam"); + assert_close(result.wetting_front_suction, 20.0, 0.01, 0.0, "matric_potential", + "centimeter_water_head", "registry", "table_2_loam"); + assert_close(result.saturated_water_content, 0.451, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "table_2_loam"); + assert_close(result.saturated_hydraulic_conductivity, 0.0417, 0.00001, 0.01, + "saturated_hydraulic_conductivity", "centimeter_per_minute", "registry", + "table_2_loam"); + assert_close(result.sorptivity, 0.693, 0.0001, 0.01, "sorptivity", + "centimeter_per_square_root_minute", "registry", "table_2_loam"); } { const clapp1978_parameters result = calc_ptf_clapp1978(clapp1978_usda_texture_class_sandy_clay_loam); - assert_close_enough(result.b, 7.12, 0.0, 0.0); - assert_close_enough(result.saturation_suction, 8.63, 0.0, 0.0); - assert_close_enough(result.wetting_front_suction, 11.7, 0.0, 0.0); - assert_close_enough(result.saturated_water_content, 0.42, 0.0, 0.0); - assert_close_enough(result.saturated_hydraulic_conductivity, 0.0378, 0.0, 0.0); - assert_close_enough(result.sorptivity, 0.488, 0.0, 0.0); + assert_close(result.b, 7.12, 0.001, 0.0, "campbell_b", "dimensionless", "registry", + "table_2_sandy_clay_loam"); + assert_close(result.saturation_suction, 8.63, 0.01, 0.0, "matric_potential", + "centimeter_water_head", "registry", "table_2_sandy_clay_loam"); + assert_close(result.wetting_front_suction, 11.7, 0.01, 0.0, "matric_potential", + "centimeter_water_head", "registry", "table_2_sandy_clay_loam"); + assert_close(result.saturated_water_content, 0.42, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "table_2_sandy_clay_loam"); + assert_close(result.saturated_hydraulic_conductivity, 0.0378, 0.00001, 0.01, + "saturated_hydraulic_conductivity", "centimeter_per_minute", "registry", + "table_2_sandy_clay_loam"); + assert_close(result.sorptivity, 0.488, 0.0001, 0.01, "sorptivity", + "centimeter_per_square_root_minute", "registry", "table_2_sandy_clay_loam"); } { const clapp1978_parameters result = calc_ptf_clapp1978(clapp1978_usda_texture_class_silty_clay_loam); - assert_close_enough(result.b, 7.75, 0.0, 0.0); - assert_close_enough(result.saturation_suction, 14.6, 0.0, 0.0); - assert_close_enough(result.wetting_front_suction, 19.7, 0.0, 0.0); - assert_close_enough(result.saturated_water_content, 0.477, 0.0, 0.0); - assert_close_enough(result.saturated_hydraulic_conductivity, 0.0102, 0.0, 0.0); - assert_close_enough(result.sorptivity, 0.31, 0.0, 0.0); + assert_close(result.b, 7.75, 0.001, 0.0, "campbell_b", "dimensionless", "registry", + "table_2_silty_clay_loam"); + assert_close(result.saturation_suction, 14.6, 0.01, 0.0, "matric_potential", + "centimeter_water_head", "registry", "table_2_silty_clay_loam"); + assert_close(result.wetting_front_suction, 19.7, 0.01, 0.0, "matric_potential", + "centimeter_water_head", "registry", "table_2_silty_clay_loam"); + assert_close(result.saturated_water_content, 0.477, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "table_2_silty_clay_loam"); + assert_close(result.saturated_hydraulic_conductivity, 0.0102, 0.00001, 0.01, + "saturated_hydraulic_conductivity", "centimeter_per_minute", "registry", + "table_2_silty_clay_loam"); + assert_close(result.sorptivity, 0.31, 0.0001, 0.01, "sorptivity", + "centimeter_per_square_root_minute", "registry", "table_2_silty_clay_loam"); } { const clapp1978_parameters result = calc_ptf_clapp1978(clapp1978_usda_texture_class_clay_loam); - assert_close_enough(result.b, 8.52, 0.0, 0.0); - assert_close_enough(result.saturation_suction, 36.1, 0.0, 0.0); - assert_close_enough(result.wetting_front_suction, 48.1, 0.0, 0.0); - assert_close_enough(result.saturated_water_content, 0.476, 0.0, 0.0); - assert_close_enough(result.saturated_hydraulic_conductivity, 0.0147, 0.0, 0.0); - assert_close_enough(result.sorptivity, 0.537, 0.0, 0.0); + assert_close(result.b, 8.52, 0.001, 0.0, "campbell_b", "dimensionless", "registry", + "table_2_clay_loam"); + assert_close(result.saturation_suction, 36.1, 0.01, 0.0, "matric_potential", + "centimeter_water_head", "registry", "table_2_clay_loam"); + assert_close(result.wetting_front_suction, 48.1, 0.01, 0.0, "matric_potential", + "centimeter_water_head", "registry", "table_2_clay_loam"); + assert_close(result.saturated_water_content, 0.476, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "table_2_clay_loam"); + assert_close(result.saturated_hydraulic_conductivity, 0.0147, 0.00001, 0.01, + "saturated_hydraulic_conductivity", "centimeter_per_minute", "registry", + "table_2_clay_loam"); + assert_close(result.sorptivity, 0.537, 0.0001, 0.01, "sorptivity", + "centimeter_per_square_root_minute", "registry", "table_2_clay_loam"); } { const clapp1978_parameters result = calc_ptf_clapp1978(clapp1978_usda_texture_class_sandy_clay); - assert_close_enough(result.b, 10.4, 0.0, 0.0); - assert_close_enough(result.saturation_suction, 6.16, 0.0, 0.0); - assert_close_enough(result.wetting_front_suction, 8.18, 0.0, 0.0); - assert_close_enough(result.saturated_water_content, 0.426, 0.0, 0.0); - assert_close_enough(result.saturated_hydraulic_conductivity, 0.013, 0.0, 0.0); - assert_close_enough(result.sorptivity, 0.223, 0.0, 0.0); + assert_close(result.b, 10.4, 0.001, 0.0, "campbell_b", "dimensionless", "registry", + "table_2_sandy_clay"); + assert_close(result.saturation_suction, 6.16, 0.01, 0.0, "matric_potential", + "centimeter_water_head", "registry", "table_2_sandy_clay"); + assert_close(result.wetting_front_suction, 8.18, 0.01, 0.0, "matric_potential", + "centimeter_water_head", "registry", "table_2_sandy_clay"); + assert_close(result.saturated_water_content, 0.426, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "table_2_sandy_clay"); + assert_close(result.saturated_hydraulic_conductivity, 0.013, 0.00001, 0.01, + "saturated_hydraulic_conductivity", "centimeter_per_minute", "registry", + "table_2_sandy_clay"); + assert_close(result.sorptivity, 0.223, 0.0001, 0.01, "sorptivity", + "centimeter_per_square_root_minute", "registry", "table_2_sandy_clay"); } { const clapp1978_parameters result = calc_ptf_clapp1978(clapp1978_usda_texture_class_silty_clay); - assert_close_enough(result.b, 10.4, 0.0, 0.0); - assert_close_enough(result.saturation_suction, 17.4, 0.0, 0.0); - assert_close_enough(result.wetting_front_suction, 23.0, 0.0, 0.0); - assert_close_enough(result.saturated_water_content, 0.492, 0.0, 0.0); - assert_close_enough(result.saturated_hydraulic_conductivity, 0.0062, 0.0, 0.0); - assert_close_enough(result.sorptivity, 0.242, 0.0, 0.0); + assert_close(result.b, 10.4, 0.001, 0.0, "campbell_b", "dimensionless", "registry", + "table_2_silty_clay"); + assert_close(result.saturation_suction, 17.4, 0.01, 0.0, "matric_potential", + "centimeter_water_head", "registry", "table_2_silty_clay"); + assert_close(result.wetting_front_suction, 23.0, 0.01, 0.0, "matric_potential", + "centimeter_water_head", "registry", "table_2_silty_clay"); + assert_close(result.saturated_water_content, 0.492, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "table_2_silty_clay"); + assert_close(result.saturated_hydraulic_conductivity, 0.0062, 0.00001, 0.01, + "saturated_hydraulic_conductivity", "centimeter_per_minute", "registry", + "table_2_silty_clay"); + assert_close(result.sorptivity, 0.242, 0.0001, 0.01, "sorptivity", + "centimeter_per_square_root_minute", "registry", "table_2_silty_clay"); } { const clapp1978_parameters result = calc_ptf_clapp1978(clapp1978_usda_texture_class_clay); - assert_close_enough(result.b, 11.4, 0.0, 0.0); - assert_close_enough(result.saturation_suction, 18.6, 0.0, 0.0); - assert_close_enough(result.wetting_front_suction, 24.3, 0.0, 0.0); - assert_close_enough(result.saturated_water_content, 0.482, 0.0, 0.0); - assert_close_enough(result.saturated_hydraulic_conductivity, 0.0077, 0.0, 0.0); - assert_close_enough(result.sorptivity, 0.268, 0.0, 0.0); + assert_close(result.b, 11.4, 0.001, 0.0, "campbell_b", "dimensionless", "registry", + "table_2_clay"); + assert_close(result.saturation_suction, 18.6, 0.01, 0.0, "matric_potential", + "centimeter_water_head", "registry", "table_2_clay"); + assert_close(result.wetting_front_suction, 24.3, 0.01, 0.0, "matric_potential", + "centimeter_water_head", "registry", "table_2_clay"); + assert_close(result.saturated_water_content, 0.482, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "table_2_clay"); + assert_close(result.saturated_hydraulic_conductivity, 0.0077, 0.00001, 0.01, + "saturated_hydraulic_conductivity", "centimeter_per_minute", "registry", + "table_2_clay"); + assert_close(result.sorptivity, 0.268, 0.0001, 0.01, "sorptivity", + "centimeter_per_square_root_minute", "registry", "table_2_clay"); } return 0; } diff --git a/targets/ptfkit-native/tests/c/comparator.c b/targets/ptfkit-native/tests/c/comparator.c new file mode 100644 index 0000000..f55b0f4 --- /dev/null +++ b/targets/ptfkit-native/tests/c/comparator.c @@ -0,0 +1,22 @@ +/* @generated by ptfkit-codegen; DO NOT EDIT. */ + +#include "support/close_enough.h" + +int main(void) { + const double absolute = 0.001; + const double relative = 0.01; + const double expected_values[] = {0.0, 2.0, -2.0}; + for (int index = 0; index < 3; ++index) { + const double expected = expected_values[index]; + const double tolerance = resolved_tolerance(expected, absolute, relative); + _close_enough_impl(__FILE__, __LINE__, expected + tolerance * 0.5, expected, absolute, + relative, "test_quantity", "1", "registry", "comparator"); + if (is_close(expected + tolerance * 2.0, expected, absolute, relative)) { + return EXIT_FAILURE; + } + } + if (is_close(NAN, 1.0, absolute, relative)) { + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} diff --git a/targets/ptfkit-native/tests/c/cosby1984.c b/targets/ptfkit-native/tests/c/cosby1984.c index b2f8194..461ef44 100644 --- a/targets/ptfkit-native/tests/c/cosby1984.c +++ b/targets/ptfkit-native/tests/c/cosby1984.c @@ -1,30 +1,48 @@ /* @generated by ptfkit-codegen; DO NOT EDIT. */ #include -#include "close_enough.h" +#include "support/close_enough.h" int main() { { const cosby1984_univariate_ptf_result result = calc_ptf_cosby1984_univariate(50.0, 30.0, 20.0); - assert_close_enough(result.mean_b, 6.09, 0.000000000001, 0.000000000001); - assert_close_enough(result.mean_log_psi_s, 1.225, 0.000000000001, 0.000000000001); - assert_close_enough(result.mean_log_k_sat, -0.119, 0.000000000001, 0.000000000001); - assert_close_enough(result.mean_theta_s, 42.6, 0.000000000001, 0.000000000001); - assert_close_enough(result.sd_b, 2.34, 0.000000000001, 0.000000000001); - assert_close_enough(result.sd_log_k_sat, 0.5553, 0.000000000001, 0.000000000001); - assert_close_enough(result.sd_theta_s, 6.27, 0.000000000001, 0.000000000001); + assert_close(result.mean_b, 6.09, 0.001, 0.0, "campbell_b", "dimensionless", "registry", + "scalar_mid_texture"); + assert_close(result.mean_log_psi_s, 1.225, 0.001, 0.0, "log_saturation_potential", + "reported_log_value", "registry", "scalar_mid_texture"); + assert_close(result.mean_log_k_sat, -0.119, 0.001, 0.0, + "log_saturated_hydraulic_conductivity", "reported_log_value", "registry", + "scalar_mid_texture"); + assert_close(result.mean_theta_s, 42.6, 0.1, 0.0, "volumetric_water_content", + "volume_percent", "registry", "scalar_mid_texture"); + assert_close(result.sd_b, 2.34, 0.001, 0.0, "campbell_b", "dimensionless", "registry", + "scalar_mid_texture"); + assert_close(result.sd_log_k_sat, 0.5553, 0.001, 0.0, + "log_saturated_hydraulic_conductivity", "reported_log_value", "registry", + "scalar_mid_texture"); + assert_close(result.sd_theta_s, 6.27, 0.1, 0.0, "volumetric_water_content", + "volume_percent", "registry", "scalar_mid_texture"); } { const cosby1984_univariate_ptf_result result = calc_ptf_cosby1984_univariate(80.0, 15.0, 5.0); - assert_close_enough(result.mean_b, 3.705, 0.000000000001, 0.000000000001); - assert_close_enough(result.mean_log_psi_s, 0.832, 0.000000000001, 0.000000000001); - assert_close_enough(result.mean_log_k_sat, 0.34, 0.000000000001, 0.000000000001); - assert_close_enough(result.mean_theta_s, 38.82, 0.000000000001, 0.000000000001); - assert_close_enough(result.sd_b, 1.59, 0.000000000001, 0.000000000001); - assert_close_enough(result.sd_log_k_sat, 0.50715, 0.000000000001, 0.000000000001); - assert_close_enough(result.sd_theta_s, 7.365, 0.000000000001, 0.000000000001); + assert_close(result.mean_b, 3.705, 0.001, 0.0, "campbell_b", "dimensionless", "registry", + "scalar_sandy_texture"); + assert_close(result.mean_log_psi_s, 0.832, 0.001, 0.0, "log_saturation_potential", + "reported_log_value", "registry", "scalar_sandy_texture"); + assert_close(result.mean_log_k_sat, 0.34, 0.001, 0.0, + "log_saturated_hydraulic_conductivity", "reported_log_value", "registry", + "scalar_sandy_texture"); + assert_close(result.mean_theta_s, 38.82, 0.1, 0.0, "volumetric_water_content", + "volume_percent", "registry", "scalar_sandy_texture"); + assert_close(result.sd_b, 1.59, 0.001, 0.0, "campbell_b", "dimensionless", "registry", + "scalar_sandy_texture"); + assert_close(result.sd_log_k_sat, 0.50715, 0.001, 0.0, + "log_saturated_hydraulic_conductivity", "reported_log_value", "registry", + "scalar_sandy_texture"); + assert_close(result.sd_theta_s, 7.365, 0.1, 0.0, "volumetric_water_content", + "volume_percent", "registry", "scalar_sandy_texture"); } return 0; } diff --git a/targets/ptfkit-native/tests/c/dharumarajan2019.c b/targets/ptfkit-native/tests/c/dharumarajan2019.c index aa25a48..3ce5d42 100644 --- a/targets/ptfkit-native/tests/c/dharumarajan2019.c +++ b/targets/ptfkit-native/tests/c/dharumarajan2019.c @@ -1,38 +1,45 @@ /* @generated by ptfkit-codegen; DO NOT EDIT. */ #include -#include "close_enough.h" +#include "support/close_enough.h" int main() { { const dharumarajan2019_water_retention_result result = calc_ptf_dharumarajan2019_nkp(43.2, 39.8, 33.6); - assert_close_enough(result.field_capacity, 29.7912, 0.000000000001, 0.000000000001); - assert_close_enough(result.permanent_wilting_point, 18.9276, 0.000000000001, - 0.000000000001); + assert_close(result.field_capacity, 29.7912, 0.1, 0.0, "gravimetric_water_content", + "mass_percent", "registry", "northern_study_mean_case"); + assert_close(result.permanent_wilting_point, 18.9276, 0.1, 0.0, "gravimetric_water_content", + "mass_percent", "registry", "northern_study_mean_case"); } { const dharumarajan2019_water_retention_result result = calc_ptf_dharumarajan2019_nkp_clay(43.2); - assert_close_enough(result.field_capacity, 30.2832, 0.000000000001, 0.000000000001); - assert_close_enough(result.permanent_wilting_point, 19.157, 0.000000000001, 0.000000000001); + assert_close(result.field_capacity, 30.2832, 0.1, 0.0, "gravimetric_water_content", + "mass_percent", "registry", "northern_study_mean_clay_case"); + assert_close(result.permanent_wilting_point, 19.157, 0.1, 0.0, "gravimetric_water_content", + "mass_percent", "registry", "northern_study_mean_clay_case"); } { const dharumarajan2019_water_retention_result result = calc_ptf_dharumarajan2019_skp(31.5, 53.5, 14.7); - assert_close_enough(result.field_capacity, 21.8169, 0.000000000001, 0.000000000001); - assert_close_enough(result.permanent_wilting_point, 11.3054, 0.000000000001, - 0.000000000001); + assert_close(result.field_capacity, 21.8169, 0.1, 0.0, "gravimetric_water_content", + "mass_percent", "registry", "southern_study_mean_case"); + assert_close(result.permanent_wilting_point, 11.3054, 0.1, 0.0, "gravimetric_water_content", + "mass_percent", "registry", "southern_study_mean_case"); } { const dharumarajan2019_water_retention_result result = calc_ptf_dharumarajan2019_skp_clay(31.5); - assert_close_enough(result.field_capacity, 22.0255, 0.000000000001, 0.000000000001); - assert_close_enough(result.permanent_wilting_point, 11.503, 0.000000000001, 0.000000000001); + assert_close(result.field_capacity, 22.0255, 0.1, 0.0, "gravimetric_water_content", + "mass_percent", "registry", "southern_study_mean_clay_case"); + assert_close(result.permanent_wilting_point, 11.503, 0.1, 0.0, "gravimetric_water_content", + "mass_percent", "registry", "southern_study_mean_clay_case"); } { const double result = calc_ptf_dharumarajan2019_infiltration(50.0, 20.0, 30.0); - assert_close_enough(result, 18.45, 0.000000000001, 0.000000000001); + assert_close(result, 18.45, 0.0001, 0.01, "infiltration_rate", "millimeter_per_hour", + "registry", "balanced_texture_case"); } return 0; } diff --git a/targets/ptfkit-native/tests/c/ferrerjulia2004.c b/targets/ptfkit-native/tests/c/ferrerjulia2004.c index 85c1fe1..a2fb72c 100644 --- a/targets/ptfkit-native/tests/c/ferrerjulia2004.c +++ b/targets/ptfkit-native/tests/c/ferrerjulia2004.c @@ -1,190 +1,232 @@ /* @generated by ptfkit-codegen; DO NOT EDIT. */ #include -#include "close_enough.h" +#include "support/close_enough.h" int main() { { const double result = calc_ptf_ferrerjulia2004_campbell_shiozawa(50.0, 25.0); - assert_close_enough(result, 0.025071690110308933, 0.000000000001, 0.000000000001); + assert_close(result, 0.025071690110308933, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const double result = calc_ptf_ferrerjulia2004_saxton(50.0, 25.0); - assert_close_enough(result, 0.00022673430784327228, 0.000000000001, 0.000000000001); + assert_close(result, 0.00022673430784327228, 0.0001, 0.01, + "saturated_hydraulic_conductivity", "millimeter_per_hour", "registry", + "reviewer_reference_vector"); } { const double result = calc_ptf_ferrerjulia2004_dane_puckett(25.0); - assert_close_enough(result, 8.302039828385373, 0.000000000001, 0.000000000001); + assert_close(result, 8.302039828385373, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const double result = calc_ptf_ferrerjulia2004_puckett(25.0); - assert_close_enough(result, 1.1257967371765654, 0.000000000001, 0.000000000001); + assert_close(result, 1.1257967371765654, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const double result = calc_ptf_ferrerjulia2004_cosby(50.0, 25.0); - assert_close_enough(result, 26.091830970918934, 0.000000000001, 0.000000000001); + assert_close(result, 26.091830970918934, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const double result = calc_ptf_ferrerjulia2004_humic_acrisol_sand(50.0); - assert_close_enough(result, 14.529879794648146, 0.000000000001, 0.000000000001); + assert_close(result, 14.529879794648146, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const double result = calc_ptf_ferrerjulia2004_humic_acrisol_texture_organic_matter(50.0, 25.0, 2.5); - assert_close_enough(result, 19.563475, 0.000000000001, 0.000000000001); + assert_close(result, 19.563475, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const double result = calc_ptf_ferrerjulia2004_calcic_cambisol_sand(50.0); - assert_close_enough(result, 9.995162000935244, 0.000000000001, 0.000000000001); + assert_close(result, 9.995162000935244, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const double result = calc_ptf_ferrerjulia2004_calcic_cambisol_texture_organic_matter(50.0, 25.0, 2.5); - assert_close_enough(result, 16.51625, 0.000000000001, 0.000000000001); + assert_close(result, 16.51625, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const double result = calc_ptf_ferrerjulia2004_dystric_cambisol_sand(50.0); - assert_close_enough(result, 13.413573870265868, 0.000000000001, 0.000000000001); + assert_close(result, 13.413573870265868, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const double result = calc_ptf_ferrerjulia2004_dystric_cambisol_texture_organic_matter(50.0, 25.0, 2.5); - assert_close_enough(result, 20.79, 0.000000000001, 0.000000000001); + assert_close(result, 20.79, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const double result = calc_ptf_ferrerjulia2004_eutric_cambisol_sand(50.0); - assert_close_enough(result, 11.570199173737363, 0.000000000001, 0.000000000001); + assert_close(result, 11.570199173737363, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const double result = calc_ptf_ferrerjulia2004_eutric_cambisol_texture_organic_matter(50.0, 25.0, 2.5); - assert_close_enough(result, 19.5235, 0.000000000001, 0.000000000001); + assert_close(result, 19.5235, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const double result = calc_ptf_ferrerjulia2004_gleyic_cambisol_sand(50.0); - assert_close_enough(result, 6.559110321079926, 0.000000000001, 0.000000000001); + assert_close(result, 6.559110321079926, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const double result = calc_ptf_ferrerjulia2004_gleyic_cambisol_texture_organic_matter(50.0, 25.0, 2.5); - assert_close_enough(result, 11.514, 0.000000000001, 0.000000000001); + assert_close(result, 11.514, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const double result = calc_ptf_ferrerjulia2004_humic_cambisol_sand(50.0); - assert_close_enough(result, 13.820129957492508, 0.000000000001, 0.000000000001); + assert_close(result, 13.820129957492508, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const double result = calc_ptf_ferrerjulia2004_humic_cambisol_texture_organic_matter(50.0, 25.0, 2.5); - assert_close_enough(result, 20.9029, 0.000000000001, 0.000000000001); + assert_close(result, 20.9029, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const double result = calc_ptf_ferrerjulia2004_calcaric_fluvisol_sand(50.0); - assert_close_enough(result, 10.959482760491376, 0.000000000001, 0.000000000001); + assert_close(result, 10.959482760491376, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const double result = calc_ptf_ferrerjulia2004_calcaric_fluvisol_texture_organic_matter(50.0, 25.0, 2.5); - assert_close_enough(result, 13.101, 0.000000000001, 0.000000000001); + assert_close(result, 13.101, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const double result = calc_ptf_ferrerjulia2004_calcic_luvisol_sand(50.0); - assert_close_enough(result, 6.931067612606957, 0.000000000001, 0.000000000001); + assert_close(result, 6.931067612606957, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const double result = calc_ptf_ferrerjulia2004_calcic_luvisol_texture_organic_matter(50.0, 25.0, 2.5); - assert_close_enough(result, 8.9332, 0.000000000001, 0.000000000001); + assert_close(result, 8.9332, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const double result = calc_ptf_ferrerjulia2004_chromic_luvisol_sand(50.0); - assert_close_enough(result, 10.61655727277843, 0.000000000001, 0.000000000001); + assert_close(result, 10.61655727277843, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const double result = calc_ptf_ferrerjulia2004_chromic_luvisol_texture_organic_matter(50.0, 25.0, 2.5); - assert_close_enough(result, 17.1465, 0.000000000001, 0.000000000001); + assert_close(result, 17.1465, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const double result = calc_ptf_ferrerjulia2004_gleyic_luvisol_sand(50.0); - assert_close_enough(result, 7.049012285884197, 0.000000000001, 0.000000000001); + assert_close(result, 7.049012285884197, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const double result = calc_ptf_ferrerjulia2004_gleyic_luvisol_texture_organic_matter(50.0, 25.0, 2.5); - assert_close_enough(result, 19.55, 0.000000000001, 0.000000000001); + assert_close(result, 19.55, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const double result = calc_ptf_ferrerjulia2004_orthic_luvisol_sand(50.0); - assert_close_enough(result, 12.04280911180458, 0.000000000001, 0.000000000001); + assert_close(result, 12.04280911180458, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const double result = calc_ptf_ferrerjulia2004_orthic_luvisol_texture_organic_matter(50.0, 25.0, 2.5); - assert_close_enough(result, 21.40425, 0.000000000001, 0.000000000001); + assert_close(result, 21.40425, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const double result = calc_ptf_ferrerjulia2004_ranker_sand(50.0); - assert_close_enough(result, 12.423399456061938, 0.000000000001, 0.000000000001); + assert_close(result, 12.423399456061938, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const double result = calc_ptf_ferrerjulia2004_ranker_texture_organic_matter(50.0, 25.0, 2.5); - assert_close_enough(result, 22.4515, 0.000000000001, 0.000000000001); + assert_close(result, 22.4515, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const double result = calc_ptf_ferrerjulia2004_calcaric_regosol_sand(50.0); - assert_close_enough(result, 12.267374733833096, 0.000000000001, 0.000000000001); + assert_close(result, 12.267374733833096, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const double result = calc_ptf_ferrerjulia2004_calcaric_regosol_texture_organic_matter(50.0, 25.0, 2.5); - assert_close_enough(result, 20.532, 0.000000000001, 0.000000000001); + assert_close(result, 20.532, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const double result = calc_ptf_ferrerjulia2004_dystric_regosol_sand(50.0); - assert_close_enough(result, 12.081401313183196, 0.000000000001, 0.000000000001); + assert_close(result, 12.081401313183196, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const double result = calc_ptf_ferrerjulia2004_dystric_regosol_texture_organic_matter(50.0, 25.0, 2.5); - assert_close_enough(result, 17.1335, 0.000000000001, 0.000000000001); + assert_close(result, 17.1335, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const double result = calc_ptf_ferrerjulia2004_eutric_regosol_sand(50.0); - assert_close_enough(result, 13.674049986082728, 0.000000000001, 0.000000000001); + assert_close(result, 13.674049986082728, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const double result = calc_ptf_ferrerjulia2004_eutric_regosol_texture_organic_matter(50.0, 25.0, 2.5); - assert_close_enough(result, 24.06375, 0.000000000001, 0.000000000001); + assert_close(result, 24.06375, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const double result = calc_ptf_ferrerjulia2004_rendzina_sand(50.0); - assert_close_enough(result, 12.515763717130312, 0.000000000001, 0.000000000001); + assert_close(result, 12.515763717130312, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const double result = calc_ptf_ferrerjulia2004_rendzina_texture_organic_matter(50.0, 25.0, 2.5); - assert_close_enough(result, 16.2905, 0.000000000001, 0.000000000001); + assert_close(result, 16.2905, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const double result = calc_ptf_ferrerjulia2004_gleyic_solonchak_sand(50.0); - assert_close_enough(result, 4.883324693640427, 0.000000000001, 0.000000000001); + assert_close(result, 4.883324693640427, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const double result = calc_ptf_ferrerjulia2004_gleyic_solonchak_texture_organic_matter(50.0, 25.0, 2.5); - assert_close_enough(result, 10.53583, 0.000000000001, 0.000000000001); + assert_close(result, 10.53583, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const double result = calc_ptf_ferrerjulia2004_general_sand(50.0); - assert_close_enough(result, 10.714718864969111, 0.000000000001, 0.000000000001); + assert_close(result, 10.714718864969111, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const double result = calc_ptf_ferrerjulia2004_general_texture_organic_matter(50.0, 25.0, 2.5); - assert_close_enough(result, 20.06325, 0.000000000001, 0.000000000001); + assert_close(result, 20.06325, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } return 0; } diff --git a/targets/ptfkit-native/tests/c/hodnett2002.c b/targets/ptfkit-native/tests/c/hodnett2002.c index 422bee9..ed14196 100644 --- a/targets/ptfkit-native/tests/c/hodnett2002.c +++ b/targets/ptfkit-native/tests/c/hodnett2002.c @@ -1,16 +1,20 @@ /* @generated by ptfkit-codegen; DO NOT EDIT. */ #include -#include "close_enough.h" +#include "support/close_enough.h" int main() { { const hodnett2002_ptf_result result = calc_ptf_hodnett2002(39.2, 24.2, 36.7, 1.4, 1.2, 15.9, 5.85); - assert_close_enough(result.alpha, 0.245183615037873, 0.000000000001, 0.000000000001); - assert_close_enough(result.n, 1.36739326352383, 0.000000000001, 0.000000000001); - assert_close_enough(result.theta_s, 0.4993353, 0.000000000001, 0.000000000001); - assert_close_enough(result.theta_r, 0.21344216, 0.000000000001, 0.000000000001); + assert_close(result.alpha, 0.245183615037873, 0.00001, 0.0, "van_genuchten_alpha", + "inverse_kilopascal", "registry", "calibration_dataset_mean_properties"); + assert_close(result.n, 1.36739326352383, 0.001, 0.0, "van_genuchten_n", "dimensionless", + "registry", "calibration_dataset_mean_properties"); + assert_close(result.theta_s, 0.4993353, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "calibration_dataset_mean_properties"); + assert_close(result.theta_r, 0.21344216, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "calibration_dataset_mean_properties"); } return 0; } diff --git a/targets/ptfkit-native/tests/c/jabro1992.c b/targets/ptfkit-native/tests/c/jabro1992.c index 77eb058..44650de 100644 --- a/targets/ptfkit-native/tests/c/jabro1992.c +++ b/targets/ptfkit-native/tests/c/jabro1992.c @@ -1,24 +1,31 @@ /* @generated by ptfkit-codegen; DO NOT EDIT. */ #include -#include "close_enough.h" +#include "support/close_enough.h" int main() { { const double result = calc_ptf_jabro1992(10.0, 5.0, 1.26); - assert_close_enough(result, 0.0003849640675896946, 0.000000000001, 0.00000001); + assert_close(result, 0.0003849640675896946, 0.0000000001, 0.01, + "saturated_hydraulic_conductivity", "meter_per_second", "registry", + "loamy_sand_min_bd"); } { const double result = calc_ptf_jabro1992(38.72, 11.05, 1.42); - assert_close_enough(result, 0.000009804037952717678, 0.000000000001, 0.00000001); + assert_close(result, 0.000009804037952717678, 0.0000000001, 0.01, + "saturated_hydraulic_conductivity", "meter_per_second", "registry", "loam"); } { const double result = calc_ptf_jabro1992(52.0, 30.0, 1.97); - assert_close_enough(result, 0.000000007292435947882127, 0.000000000001, 0.00000001); + assert_close(result, 0.000000007292435947882127, 0.0000000001, 0.01, + "saturated_hydraulic_conductivity", "meter_per_second", "registry", + "silty_clay_loam_max_silt"); } { const double result = calc_ptf_jabro1992(0.2, 44.0, 1.61); - assert_close_enough(result, 0.00002032824027706267, 0.000000000001, 0.00000001); + assert_close(result, 0.00002032824027706267, 0.0000000001, 0.01, + "saturated_hydraulic_conductivity", "meter_per_second", "registry", + "sandy_clay_min_silt_max_clay"); } return 0; } diff --git a/targets/ptfkit-native/tests/c/li2007.c b/targets/ptfkit-native/tests/c/li2007.c index 7cf4e08..20a91a5 100644 --- a/targets/ptfkit-native/tests/c/li2007.c +++ b/targets/ptfkit-native/tests/c/li2007.c @@ -1,29 +1,43 @@ /* @generated by ptfkit-codegen; DO NOT EDIT. */ #include -#include "close_enough.h" +#include "support/close_enough.h" int main() { { const li2007_ptf_result result = calc_ptf_li2007(85.0, 10.0, 5.0, 1.2, 0.21); - assert_close_enough(result.theta_s, 0.5256803583157499, 0.000000000001, 0.00000001); - assert_close_enough(result.a_vg, 0.9491464758307142, 0.000000000001, 0.00000001); - assert_close_enough(result.n_vg, 1.1657804980997006, 0.000000000001, 0.00000001); - assert_close_enough(result.k_sat, 0.000006549110367333547, 0.000000000001, 0.00000001); + assert_close(result.theta_s, 0.5256803583157499, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "loamy_sand"); + assert_close(result.a_vg, 0.9491464758307142, 0.00001, 0.0, "van_genuchten_alpha", + "inverse_centimeter", "registry", "loamy_sand"); + assert_close(result.n_vg, 1.1657804980997006, 0.001, 0.0, "van_genuchten_n", + "dimensionless", "registry", "loamy_sand"); + assert_close(result.k_sat, 0.000006549110367333547, 0.0000000001, 0.01, + "saturated_hydraulic_conductivity", "meter_per_second", "registry", + "loamy_sand"); } { const li2007_ptf_result result = calc_ptf_li2007(50.23, 38.72, 11.05, 1.42, 0.65); - assert_close_enough(result.theta_s, 0.49659526127697506, 0.000000000001, 0.00000001); - assert_close_enough(result.a_vg, 0.009519989841950734, 0.000000000001, 0.00000001); - assert_close_enough(result.n_vg, 1.1806286355149054, 0.000000000001, 0.00000001); - assert_close_enough(result.k_sat, 0.00000045117324656202257, 0.000000000001, 0.00000001); + assert_close(result.theta_s, 0.49659526127697506, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "loam"); + assert_close(result.a_vg, 0.009519989841950734, 0.00001, 0.0, "van_genuchten_alpha", + "inverse_centimeter", "registry", "loam"); + assert_close(result.n_vg, 1.1806286355149054, 0.001, 0.0, "van_genuchten_n", + "dimensionless", "registry", "loam"); + assert_close(result.k_sat, 0.00000045117324656202257, 0.0000000001, 0.01, + "saturated_hydraulic_conductivity", "meter_per_second", "registry", "loam"); } { const li2007_ptf_result result = calc_ptf_li2007(12.88, 60.0, 27.12, 1.48, 1.02); - assert_close_enough(result.theta_s, 0.4053061510618609, 0.000000000001, 0.00000001); - assert_close_enough(result.a_vg, 0.0018530400762371828, 0.000000000001, 0.00000001); - assert_close_enough(result.n_vg, 1.2080428739797433, 0.000000000001, 0.00000001); - assert_close_enough(result.k_sat, 0.0000015151432632107234, 0.000000000001, 0.00000001); + assert_close(result.theta_s, 0.4053061510618609, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "silty_clay"); + assert_close(result.a_vg, 0.0018530400762371828, 0.00001, 0.0, "van_genuchten_alpha", + "inverse_centimeter", "registry", "silty_clay"); + assert_close(result.n_vg, 1.2080428739797433, 0.001, 0.0, "van_genuchten_n", + "dimensionless", "registry", "silty_clay"); + assert_close(result.k_sat, 0.0000015151432632107234, 0.0000000001, 0.01, + "saturated_hydraulic_conductivity", "meter_per_second", "registry", + "silty_clay"); } return 0; } diff --git a/targets/ptfkit-native/tests/c/mayr1999.c b/targets/ptfkit-native/tests/c/mayr1999.c index acd8b53..892f2e6 100644 --- a/targets/ptfkit-native/tests/c/mayr1999.c +++ b/targets/ptfkit-native/tests/c/mayr1999.c @@ -1,26 +1,35 @@ /* @generated by ptfkit-codegen; DO NOT EDIT. */ #include -#include "close_enough.h" +#include "support/close_enough.h" int main() { { const mayr1999_ptf_result result = calc_ptf_mayr1999(40.0, 40.0, 20.0, 1.3, 2.0); - assert_close_enough(result.a_hc, 9.19922782578422, 0.000000000001, 0.000000000001); - assert_close_enough(result.b_hc, 8.52432443785475, 0.000000000001, 0.000000000001); - assert_close_enough(result.theta_s, 0.48733161333, 0.000000000001, 0.000000000001); + assert_close(result.a_hc, 9.19922782578422, 0.01, 0.0, "matric_potential", + "centimeter_water_head", "registry", "representative_loam"); + assert_close(result.b_hc, 8.52432443785475, 0.001, 0.0, "hutson_cass_b", "dimensionless", + "registry", "representative_loam"); + assert_close(result.theta_s, 0.48733161333, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "representative_loam"); } { const mayr1999_ptf_result result = calc_ptf_mayr1999(20.0, 20.0, 60.0, 1.2, 2.0); - assert_close_enough(result.a_hc, 14.5305137640911, 0.000000000001, 0.000000000001); - assert_close_enough(result.b_hc, 18.9375141190925, 0.000000000001, 0.000000000001); - assert_close_enough(result.theta_s, 0.52145684162, 0.000000000001, 0.000000000001); + assert_close(result.a_hc, 14.5305137640911, 0.01, 0.0, "matric_potential", + "centimeter_water_head", "registry", "representative_clay"); + assert_close(result.b_hc, 18.9375141190925, 0.001, 0.0, "hutson_cass_b", "dimensionless", + "registry", "representative_clay"); + assert_close(result.theta_s, 0.52145684162, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "representative_clay"); } { const mayr1999_ptf_result result = calc_ptf_mayr1999(90.0, 5.0, 5.0, 1.5, 1.0); - assert_close_enough(result.a_hc, 2.40216699550362, 0.000000000001, 0.000000000001); - assert_close_enough(result.b_hc, 3.2690464531591, 0.000000000001, 0.000000000001); - assert_close_enough(result.theta_s, 0.4209756915, 0.000000000001, 0.000000000001); + assert_close(result.a_hc, 2.40216699550362, 0.01, 0.0, "matric_potential", + "centimeter_water_head", "registry", "representative_sand"); + assert_close(result.b_hc, 3.2690464531591, 0.001, 0.0, "hutson_cass_b", "dimensionless", + "registry", "representative_sand"); + assert_close(result.theta_s, 0.4209756915, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "representative_sand"); } return 0; } diff --git a/targets/ptfkit-native/tests/c/oosterveld1980.c b/targets/ptfkit-native/tests/c/oosterveld1980.c index 604a04b..ded70f7 100644 --- a/targets/ptfkit-native/tests/c/oosterveld1980.c +++ b/targets/ptfkit-native/tests/c/oosterveld1980.c @@ -1,28 +1,33 @@ /* @generated by ptfkit-codegen; DO NOT EDIT. */ #include -#include "close_enough.h" +#include "support/close_enough.h" int main() { { const double result = calc_ptf_oosterveld1980_field_capacity_tension(6.6); - assert_close_enough(result, 11.8540994164288, 0.000000000001, 0.000000000001); + assert_close(result, 11.8540994164288, 0.01, 0.0, "matric_potential", "kilopascal", + "registry", "table_1_loamy_sand"); } { const double result = calc_ptf_oosterveld1980_retention(6.6, 86.3, 105.0, 11.8); - assert_close_enough(result, 8.2782468100413, 0.000000000001, 0.000000000001); + assert_close(result, 8.2782468100413, 0.1, 0.0, "gravimetric_water_content", "mass_percent", + "registry", "table_1_loamy_sand_inputs"); } { const double result = calc_ptf_oosterveld1980_field_capacity(6.6, 86.3, 105.0); - assert_close_enough(result, 8.14707947394088, 0.000000000001, 0.000000000001); + assert_close(result, 8.14707947394088, 0.1, 0.0, "gravimetric_water_content", + "mass_percent", "registry", "table_1_loamy_sand"); } { const double result = calc_ptf_oosterveld1980_wilting_point(6.6, 86.3, 105.0); - assert_close_enough(result, 1.3942, 0.000000000001, 0.000000000001); + assert_close(result, 1.3942, 0.1, 0.0, "gravimetric_water_content", "mass_percent", + "registry", "table_1_loamy_sand"); } { const double result = calc_ptf_oosterveld1980_available_water(6.6, 86.3, 105.0); - assert_close_enough(result, 6.75287947394088, 0.000000000001, 0.000000000001); + assert_close(result, 6.75287947394088, 0.1, 0.0, "gravimetric_water_content", + "mass_percent", "registry", "table_1_loamy_sand"); } return 0; } diff --git a/targets/ptfkit-native/tests/c/pidgeon1972.c b/targets/ptfkit-native/tests/c/pidgeon1972.c index 62f6175..a80d6bb 100644 --- a/targets/ptfkit-native/tests/c/pidgeon1972.c +++ b/targets/ptfkit-native/tests/c/pidgeon1972.c @@ -1,76 +1,93 @@ /* @generated by ptfkit-codegen; DO NOT EDIT. */ #include -#include "close_enough.h" +#include "support/close_enough.h" int main() { { const double result = calc_ptf_pidgeon1972_fc(30.0, 20.0, 2.0); - assert_close_enough(result, 21.26, 0.000000000001, 0.000000000001); + assert_close(result, 21.26, 0.1, 0.0, "gravimetric_water_content", "mass_percent", + "registry", "regression_case"); } { const double result = calc_ptf_pidgeon1972_fc_sand(50.0); - assert_close_enough(result, 23.66, 0.000000000001, 0.000000000001); + assert_close(result, 23.66, 0.1, 0.0, "gravimetric_water_content", "mass_percent", + "registry", "regression_case"); } { const double result = calc_ptf_pidgeon1972_fc_sand_organic_matter(50.0, 2.0); - assert_close_enough(result, 23.27, 0.000000000001, 0.000000000001); + assert_close(result, 23.27, 0.1, 0.0, "gravimetric_water_content", "mass_percent", + "registry", "regression_case"); } { const double result = calc_ptf_pidgeon1972_fc_vol_sand_organic_matter(50.0, 2.0); - assert_close_enough(result, 31.19, 0.000000000001, 0.000000000001); + assert_close(result, 31.19, 0.1, 0.0, "volumetric_water_content", "volume_percent", + "registry", "regression_case"); } { const double result = calc_ptf_pidgeon1972_pwp(30.0, 20.0, 2.0); - assert_close_enough(result, 11.11, 0.000000000001, 0.000000000001); + assert_close(result, 11.11, 0.1, 0.0, "gravimetric_water_content", "mass_percent", + "registry", "regression_case"); } { const double result = calc_ptf_pidgeon1972_pwp_sand(50.0); - assert_close_enough(result, 13.91, 0.000000000001, 0.000000000001); + assert_close(result, 13.91, 0.1, 0.0, "gravimetric_water_content", "mass_percent", + "registry", "regression_case"); } { const double result = calc_ptf_pidgeon1972_pwp_sand_organic_matter(50.0, 2.0); - assert_close_enough(result, 15.28, 0.000000000001, 0.000000000001); + assert_close(result, 15.28, 0.1, 0.0, "gravimetric_water_content", "mass_percent", + "registry", "regression_case"); } { const double result = calc_ptf_pidgeon1972_awc(20.0, 2.0); - assert_close_enough(result, 151.48, 0.000000000001, 0.000000000001); + assert_close(result, 151.48, 0.1, 0.0, "available_water_capacity", "millimeter_per_meter", + "registry", "regression_case"); } { const double result = calc_ptf_pidgeon1972_awc_sand_organic_matter(50.0, 2.0); - assert_close_enough(result, 109.24, 0.000000000001, 0.000000000001); + assert_close(result, 109.24, 0.1, 0.0, "available_water_capacity", "millimeter_per_meter", + "registry", "regression_case"); } { const double result = calc_ptf_pidgeon1972_awc_coarse_sand(20.0); - assert_close_enough(result, 115.1, 0.000000000001, 0.000000000001); + assert_close(result, 115.1, 0.1, 0.0, "available_water_capacity", "millimeter_per_meter", + "registry", "regression_case"); } { const double result = calc_ptf_pidgeon1972_awc_fine_sand(20.0); - assert_close_enough(result, 119.9, 0.000000000001, 0.000000000001); + assert_close(result, 119.9, 0.1, 0.0, "available_water_capacity", "millimeter_per_meter", + "registry", "regression_case"); } { const double result = calc_ptf_pidgeon1972_awc_very_fine_sand(10.0); - assert_close_enough(result, 112.7, 0.000000000001, 0.000000000001); + assert_close(result, 112.7, 0.1, 0.0, "available_water_capacity", "millimeter_per_meter", + "registry", "regression_case"); } { const double result = calc_ptf_pidgeon1972_eawc(30.0, 20.0, 2.0); - assert_close_enough(result, 16.12, 0.000000000001, 0.000000000001); + assert_close(result, 16.12, 0.1, 0.0, "available_water_capacity", "millimeter_per_meter", + "registry", "regression_case"); } { const double result = calc_ptf_pidgeon1972_eawc_sand(50.0); - assert_close_enough(result, 51.7, 0.000000000001, 0.000000000001); + assert_close(result, 51.7, 0.1, 0.0, "available_water_capacity", "millimeter_per_meter", + "registry", "regression_case"); } { const double result = calc_ptf_pidgeon1972_eawc_sand_organic_matter(50.0, 2.0); - assert_close_enough(result, 56.26, 0.000000000001, 0.000000000001); + assert_close(result, 56.26, 0.1, 0.0, "available_water_capacity", "millimeter_per_meter", + "registry", "regression_case"); } { const double result = calc_ptf_pidgeon1972_eawc_coarse_sand_organic_matter(20.0, 2.0); - assert_close_enough(result, 53.72, 0.000000000001, 0.000000000001); + assert_close(result, 53.72, 0.1, 0.0, "available_water_capacity", "millimeter_per_meter", + "registry", "regression_case"); } { const double result = calc_ptf_pidgeon1972_eawc_fine_sand_organic_matter(20.0, 2.0); - assert_close_enough(result, 59.58, 0.000000000001, 0.000000000001); + assert_close(result, 59.58, 0.1, 0.0, "available_water_capacity", "millimeter_per_meter", + "registry", "regression_case"); } return 0; } diff --git a/targets/ptfkit-native/tests/c/puckett1985.c b/targets/ptfkit-native/tests/c/puckett1985.c index c26608a..83b5e9a 100644 --- a/targets/ptfkit-native/tests/c/puckett1985.c +++ b/targets/ptfkit-native/tests/c/puckett1985.c @@ -1,22 +1,34 @@ /* @generated by ptfkit-codegen; DO NOT EDIT. */ #include -#include "close_enough.h" +#include "support/close_enough.h" int main() { { const puckett1985_ptf_result result = calc_ptf_puckett1985(70.9, 36.4, 11.8, 1.67, 0.38); - assert_close_enough(result.theta_0, 0.34288, 0.000000000001, 0.00000001); - assert_close_enough(result.theta_1, 0.33926, 0.000000000001, 0.00000001); - assert_close_enough(result.theta_5, 0.3938615, 0.000000000001, 0.00000001); - assert_close_enough(result.theta_10, 0.39330438, 0.000000000001, 0.00000001); - assert_close_enough(result.theta_30, 0.34432936, 0.000000000001, 0.00000001); - assert_close_enough(result.theta_60, 0.31153562, 0.000000000001, 0.00000001); - assert_close_enough(result.theta_100, 0.29292896, 0.000000000001, 0.00000001); - assert_close_enough(result.theta_500, 0.2513588, 0.000000000001, 0.00000001); - assert_close_enough(result.theta_1000, 0.25187788, 0.000000000001, 0.00000001); - assert_close_enough(result.theta_1500, 0.22746346, 0.000000000001, 0.00000001); - assert_close_enough(result.k_sat, 0.0000042399741, 0.000000000001, 0.00000001); + assert_close(result.theta_0, 0.34288, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "cahaba_ap"); + assert_close(result.theta_1, 0.33926, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "cahaba_ap"); + assert_close(result.theta_5, 0.3938615, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "cahaba_ap"); + assert_close(result.theta_10, 0.39330438, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "cahaba_ap"); + assert_close(result.theta_30, 0.34432936, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "cahaba_ap"); + assert_close(result.theta_60, 0.31153562, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "cahaba_ap"); + assert_close(result.theta_100, 0.29292896, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "cahaba_ap"); + assert_close(result.theta_500, 0.2513588, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "cahaba_ap"); + assert_close(result.theta_1000, 0.25187788, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "cahaba_ap"); + assert_close(result.theta_1500, 0.22746346, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "cahaba_ap"); + assert_close(result.k_sat, 0.0000042399741, 0.0000000001, 0.01, + "saturated_hydraulic_conductivity", "meter_per_second", "registry", + "cahaba_ap"); } return 0; } diff --git a/targets/ptfkit-native/tests/c/rawls1982.c b/targets/ptfkit-native/tests/c/rawls1982.c index a15fdb3..8a4fd3c 100644 --- a/targets/ptfkit-native/tests/c/rawls1982.c +++ b/targets/ptfkit-native/tests/c/rawls1982.c @@ -1,32 +1,46 @@ /* @generated by ptfkit-codegen; DO NOT EDIT. */ #include -#include "close_enough.h" +#include "support/close_enough.h" int main() { { const double result = calc_ptf_rawls1982_theta_1500(5.12, 0.1); - assert_close_enough(result, 0.05318, 0.000000000001, 0.0000000001); + assert_close(result, 0.05318, 0.001, 0.0, "volumetric_water_content", "volume_fraction", + "registry", "loamy_sand"); } { const double result = calc_ptf_rawls1982_theta_33(85.0, 0.1, 0.05318); - assert_close_enough(result, 0.1179896, 0.000000000001, 0.0000000001); + assert_close(result, 0.1179896, 0.001, 0.0, "volumetric_water_content", "volume_fraction", + "registry", "loamy_sand_with_estimated_theta_1500"); } { const rawls1982_ptf_result result = calc_ptf_rawls1982_full_wrc(85.0, 0.66, 1.22, 0.091, 0.033); - assert_close_enough(result.theta_4, 0.247242, 0.000000000001, 0.0000000001); - assert_close_enough(result.theta_7, 0.968738, 0.000000000001, 0.0000000001); - assert_close_enough(result.theta_10, 0.145588, 0.000000000001, 0.0000000001); - assert_close_enough(result.theta_20, 0.10483, 0.000000000001, 0.0000000001); - assert_close_enough(result.theta_33, 0.091, 0.000000000001, 0.0000000001); - assert_close_enough(result.theta_60, 0.075428, 0.000000000001, 0.0000000001); - assert_close_enough(result.theta_100, 0.063192, 0.000000000001, 0.0000000001); - assert_close_enough(result.theta_200, 0.052946, 0.000000000001, 0.0000000001); - assert_close_enough(result.theta_400, 0.045826, 0.000000000001, 0.0000000001); - assert_close_enough(result.theta_700, 0.041824, 0.000000000001, 0.0000000001); - assert_close_enough(result.theta_1000, 0.038932, 0.000000000001, 0.0000000001); - assert_close_enough(result.theta_1500, 0.033, 0.000000000001, 0.0000000001); + assert_close(result.theta_4, 0.247242, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "loamy_sand"); + assert_close(result.theta_7, 0.968738, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "loamy_sand"); + assert_close(result.theta_10, 0.145588, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "loamy_sand"); + assert_close(result.theta_20, 0.10483, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "loamy_sand"); + assert_close(result.theta_33, 0.091, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "loamy_sand"); + assert_close(result.theta_60, 0.075428, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "loamy_sand"); + assert_close(result.theta_100, 0.063192, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "loamy_sand"); + assert_close(result.theta_200, 0.052946, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "loamy_sand"); + assert_close(result.theta_400, 0.045826, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "loamy_sand"); + assert_close(result.theta_700, 0.041824, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "loamy_sand"); + assert_close(result.theta_1000, 0.038932, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "loamy_sand"); + assert_close(result.theta_1500, 0.033, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "loamy_sand"); } return 0; } diff --git a/targets/ptfkit-native/tests/c/saxton2006.c b/targets/ptfkit-native/tests/c/saxton2006.c index edf7c80..13ee22d 100644 --- a/targets/ptfkit-native/tests/c/saxton2006.c +++ b/targets/ptfkit-native/tests/c/saxton2006.c @@ -1,74 +1,88 @@ /* @generated by ptfkit-codegen; DO NOT EDIT. */ #include -#include "close_enough.h" +#include "support/close_enough.h" int main() { { const saxton2006_ptf_result result = calc_ptf_saxton2006(0.88, 0.05, 2.5); - assert_close_enough(result.theta_1500, 0.05022058, 0.000000000001, 0.000000000001); - assert_close_enough(result.theta_33, 0.10282792364858702, 0.000000000001, 0.000000000001); - assert_close_enough(result.theta_s, 0.46172240764858724, 0.000000000001, 0.000000000001); - assert_close_enough(result.plant_available_water, 0.05260734364858702, 0.000000000001, - 0.000000000001); - assert_close_enough(result.air_entry_tension, 0.5986789729513293, 0.000000000001, - 0.000000000001); - assert_close_enough(result.retention_a, 0.00018076452552206025, 0.000000000001, - 0.000000000001); - assert_close_enough(result.retention_b, 5.325903100453899, 0.000000000001, 0.000000000001); - assert_close_enough(result.conductivity_lambda, 0.18776158355467926, 0.000000000001, - 0.000000000001); - assert_close_enough(result.saturated_conductivity, 108.1478278507403, 0.000000000001, - 0.000000000001); - assert_close_enough(result.normal_density, 1.4264356197312436, 0.000000000001, - 0.000000000001); + assert_close(result.theta_1500, 0.05022058, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "table_3_sand"); + assert_close(result.theta_33, 0.10282792364858702, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "table_3_sand"); + assert_close(result.theta_s, 0.46172240764858724, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "table_3_sand"); + assert_close(result.plant_available_water, 0.05260734364858702, 0.001, 0.0, + "volumetric_water_content", "volume_fraction", "registry", "table_3_sand"); + assert_close(result.air_entry_tension, 0.5986789729513293, 0.01, 0.0, "matric_potential", + "kilopascal", "registry", "table_3_sand"); + assert_close(result.retention_a, 0.00018076452552206025, 0.000000001, 0.01, + "retention_coefficient_a", "kilopascal", "registry", "table_3_sand"); + assert_close(result.retention_b, 5.325903100453899, 0.001, 0.0, "retention_coefficient_b", + "dimensionless", "registry", "table_3_sand"); + assert_close(result.conductivity_lambda, 0.18776158355467926, 0.001, 0.0, + "conductivity_shape_parameter", "dimensionless", "registry", "table_3_sand"); + assert_close(result.saturated_conductivity, 108.1478278507403, 0.0001, 0.01, + "saturated_hydraulic_conductivity", "millimeter_per_hour", "registry", + "table_3_sand"); + assert_close(result.normal_density, 1.4264356197312436, 0.001, 0.0, "dry_bulk_density", + "gram_per_cubic_centimeter", "registry", "table_3_sand"); } { const saxton2006_density_result result = calc_ptf_saxton2006_density( 1.4264356197312436, 0.46172240764858724, 0.10282792364858702, 1.1); - assert_close_enough(result.adjusted_density, 1.569079181704368, 0.000000000001, - 0.000000000001); - assert_close_enough(result.adjusted_theta_s, 0.407894648413446, 0.000000000001, - 0.000000000001); - assert_close_enough(result.adjusted_theta_33, 0.09206237180155877, 0.000000000001, - 0.000000000001); - assert_close_enough(result.adjusted_theta_s_minus_33, 0.3158322766118873, 0.000000000001, - 0.000000000001); + assert_close(result.adjusted_density, 1.569079181704368, 0.001, 0.0, "dry_bulk_density", + "gram_per_cubic_centimeter", "registry", "table_3_sand_compacted"); + assert_close(result.adjusted_theta_s, 0.407894648413446, 0.001, 0.0, + "volumetric_water_content", "volume_fraction", "registry", + "table_3_sand_compacted"); + assert_close(result.adjusted_theta_33, 0.09206237180155877, 0.001, 0.0, + "volumetric_water_content", "volume_fraction", "registry", + "table_3_sand_compacted"); + assert_close(result.adjusted_theta_s_minus_33, 0.3158322766118873, 0.001, 0.0, + "volumetric_water_content", "volume_fraction", "registry", + "table_3_sand_compacted"); } { const double result = calc_ptf_saxton2006_tension_dry(0.07652425182429351, 0.05022058, 0.10282792364858702); - assert_close_enough(result, 159.18094591362183, 0.000000000001, 0.000000000001); + assert_close(result, 159.18094591362183, 0.01, 0.0, "matric_potential", "kilopascal", + "registry", "table_3_sand_midpoint"); } { const double result = calc_ptf_saxton2006_tension_wet( 0.2, 0.10282792364858702, 0.46172240764858724, 0.5986789729513293); - assert_close_enough(result, 24.227216407352152, 0.000000000001, 0.000000000001); + assert_close(result, 24.227216407352152, 0.01, 0.0, "matric_potential", "kilopascal", + "registry", "table_3_sand_theta_0_2"); } { const double result = calc_ptf_saxton2006_conductivity( 0.3, 0.46172240764858724, 108.1478278507403, 0.18776158355467926); - assert_close_enough(result, 0.3003203142764693, 0.000000000001, 0.000000000001); + assert_close(result, 0.3003203142764693, 0.0001, 0.01, "unsaturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "table_3_sand_theta_0_3"); } { const saxton2006_gravel_result result = calc_ptf_saxton2006_gravel( 0.2, 1.4264356197312436, 0.05260734364858702, 108.1478278507403); - assert_close_enough(result.gravel_volume_fraction, 0.11860834455314037, 0.000000000001, - 0.000000000001); - assert_close_enough(result.bulk_density, 1.5715605653291098, 0.000000000001, - 0.000000000001); - assert_close_enough(result.bulk_plant_available_water, 0.04636767370708995, 0.000000000001, - 0.000000000001); - assert_close_enough(result.bulk_saturated_conductivity, 97.54880510583706, 0.000000000001, - 0.000000000001); + assert_close(result.gravel_volume_fraction, 0.11860834455314037, 0.001, 0.0, + "gravel_volume_fraction", "volume_fraction", "registry", + "table_3_sand_twenty_percent_gravel"); + assert_close(result.bulk_density, 1.5715605653291098, 0.001, 0.0, "dry_bulk_density", + "gram_per_cubic_centimeter", "registry", "table_3_sand_twenty_percent_gravel"); + assert_close(result.bulk_plant_available_water, 0.04636767370708995, 0.001, 0.0, + "volumetric_water_content", "volume_fraction", "registry", + "table_3_sand_twenty_percent_gravel"); + assert_close(result.bulk_saturated_conductivity, 97.54880510583706, 0.0001, 0.01, + "saturated_hydraulic_conductivity", "millimeter_per_hour", "registry", + "table_3_sand_twenty_percent_gravel"); } { const saxton2006_salinity_result result = calc_ptf_saxton2006_salinity(4.0, 0.3, 0.46172240764858724); - assert_close_enough(result.saturated_osmotic_potential, 144.0, 0.000000000001, - 0.000000000001); - assert_close_enough(result.osmotic_potential, 221.6267556713219, 0.000000000001, - 0.000000000001); + assert_close(result.saturated_osmotic_potential, 144.0, 0.01, 0.0, "osmotic_potential", + "kilopascal", "registry", "saline_partly_saturated_soil"); + assert_close(result.osmotic_potential, 221.6267556713219, 0.01, 0.0, "osmotic_potential", + "kilopascal", "registry", "saline_partly_saturated_soil"); } return 0; } diff --git a/targets/ptfkit-native/tests/c/tiwary2014.c b/targets/ptfkit-native/tests/c/tiwary2014.c index 8a6a08c..844a08a 100644 --- a/targets/ptfkit-native/tests/c/tiwary2014.c +++ b/targets/ptfkit-native/tests/c/tiwary2014.c @@ -1,20 +1,27 @@ /* @generated by ptfkit-codegen; DO NOT EDIT. */ #include -#include "close_enough.h" +#include "support/close_enough.h" int main() { { const double result = calc_ptf_tiwary2014_igp(37.3, 1.674, 4.6); - assert_close_enough(result, 0.0000005103578, 0.000000000001, 0.0000000001); + assert_close(result, 0.0000005103578, 0.0000000001, 0.01, + "saturated_hydraulic_conductivity", "meter_per_second", "registry", + "igp_saturated_conductivity_case"); } { const tiwary2014_ptf_result result = calc_ptf_tiwary2014_bsr(54.9, 7.6, 61.6, 7.3, 21.4, 3.32); - assert_close_enough(result.w_33, 41.1729, 0.000000000001, 0.0000000001); - assert_close_enough(result.w_100, 36.8273, 0.000000000001, 0.0000000001); - assert_close_enough(result.w_1500, 21.6976, 0.000000000001, 0.0000000001); - assert_close_enough(result.k_sat, 0.000005373367, 0.000000000001, 0.0000000001); + assert_close(result.w_33, 41.1729, 0.1, 0.0, "gravimetric_water_content", "mass_percent", + "registry", "black_soil_compatibility_case"); + assert_close(result.w_100, 36.8273, 0.1, 0.0, "gravimetric_water_content", "mass_percent", + "registry", "black_soil_compatibility_case"); + assert_close(result.w_1500, 21.6976, 0.1, 0.0, "gravimetric_water_content", "mass_percent", + "registry", "black_soil_compatibility_case"); + assert_close(result.k_sat, 0.000005373367, 0.0000000001, 0.01, + "saturated_hydraulic_conductivity", "meter_per_second", "registry", + "black_soil_compatibility_case"); } return 0; } diff --git a/targets/ptfkit-native/tests/c/varallyai1982.c b/targets/ptfkit-native/tests/c/varallyai1982.c index dc2c2cc..de02ce1 100644 --- a/targets/ptfkit-native/tests/c/varallyai1982.c +++ b/targets/ptfkit-native/tests/c/varallyai1982.c @@ -1,32 +1,44 @@ /* @generated by ptfkit-codegen; DO NOT EDIT. */ #include -#include "close_enough.h" +#include "support/close_enough.h" int main() { { const varallyai1982_parameters result = calc_ptf_varallyai1982_meadow(1.4, 0.3, 0.25); - assert_close_enough(result.theta_0, 46.54495, 0.000000000001, 0.000000000001); - assert_close_enough(result.m, 0.10029, 0.000000000001, 0.000000000001); - assert_close_enough(result.pf_star, 3.62445, 0.000000000001, 0.000000000001); + assert_close(result.theta_0, 46.54495, 0.1, 0.0, "volumetric_water_content", + "volume_percent", "registry", "representative_review_case"); + assert_close(result.m, 0.10029, 0.001, 0.0, "varallyai_m", "dimensionless", "registry", + "representative_review_case"); + assert_close(result.pf_star, 3.62445, 0.001, 0.0, "varallyai_pf_star", "dimensionless", + "registry", "representative_review_case"); } { const varallyai1982_parameters result = calc_ptf_varallyai1982_chernozem_a(1.4, 0.35); - assert_close_enough(result.theta_0, 52.005, 0.000000000001, 0.000000000001); - assert_close_enough(result.m, 0.4174, 0.000000000001, 0.000000000001); - assert_close_enough(result.pf_star, 4.00469, 0.000000000001, 0.000000000001); + assert_close(result.theta_0, 52.005, 0.1, 0.0, "volumetric_water_content", "volume_percent", + "registry", "representative_review_case"); + assert_close(result.m, 0.4174, 0.001, 0.0, "varallyai_m", "dimensionless", "registry", + "representative_review_case"); + assert_close(result.pf_star, 4.00469, 0.001, 0.0, "varallyai_pf_star", "dimensionless", + "registry", "representative_review_case"); } { const varallyai1982_parameters result = calc_ptf_varallyai1982_chernozem_b(1.4, 0.35); - assert_close_enough(result.theta_0, 47.60035, 0.000000000001, 0.000000000001); - assert_close_enough(result.m, 0.407, 0.000000000001, 0.000000000001); - assert_close_enough(result.pf_star, 3.9299, 0.000000000001, 0.000000000001); + assert_close(result.theta_0, 47.60035, 0.1, 0.0, "volumetric_water_content", + "volume_percent", "registry", "representative_review_case"); + assert_close(result.m, 0.407, 0.001, 0.0, "varallyai_m", "dimensionless", "registry", + "representative_review_case"); + assert_close(result.pf_star, 3.9299, 0.001, 0.0, "varallyai_pf_star", "dimensionless", + "registry", "representative_review_case"); } { const varallyai1982_parameters result = calc_ptf_varallyai1982_chernozem_c(1.4, 0.35); - assert_close_enough(result.theta_0, 49.87, 0.000000000001, 0.000000000001); - assert_close_enough(result.m, 0.84011, 0.000000000001, 0.000000000001); - assert_close_enough(result.pf_star, 3.59772, 0.000000000001, 0.000000000001); + assert_close(result.theta_0, 49.87, 0.1, 0.0, "volumetric_water_content", "volume_percent", + "registry", "representative_review_case"); + assert_close(result.m, 0.84011, 0.001, 0.0, "varallyai_m", "dimensionless", "registry", + "representative_review_case"); + assert_close(result.pf_star, 3.59772, 0.001, 0.0, "varallyai_pf_star", "dimensionless", + "registry", "representative_review_case"); } return 0; } diff --git a/targets/ptfkit-native/tests/c/vereecken1989.c b/targets/ptfkit-native/tests/c/vereecken1989.c index e94150b..2682fee 100644 --- a/targets/ptfkit-native/tests/c/vereecken1989.c +++ b/targets/ptfkit-native/tests/c/vereecken1989.c @@ -1,23 +1,31 @@ /* @generated by ptfkit-codegen; DO NOT EDIT. */ #include -#include "close_enough.h" +#include "support/close_enough.h" int main() { { const vereecken1989_ptf_result result = calc_ptf_vereecken1989(52.14, 10.93, 1.03, 1.466); - assert_close_enough(result.theta_r, 0.08406999999999999, 0.000000000001, 0.000000000001); - assert_close_enough(result.theta_s, 0.4060520000000001, 0.000000000001, 0.000000000001); - assert_close_enough(result.alpha, 0.003581613076579849, 0.000000000001, 0.000000000001); - assert_close_enough(result.n, 0.8602234826041976, 0.000000000001, 0.000000000001); + assert_close(result.theta_r, 0.08406999999999999, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "dataset_mean_properties"); + assert_close(result.theta_s, 0.4060520000000001, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "dataset_mean_properties"); + assert_close(result.alpha, 0.003581613076579849, 0.00001, 0.0, "van_genuchten_alpha", + "inverse_centimeter", "registry", "dataset_mean_properties"); + assert_close(result.n, 0.8602234826041976, 0.001, 0.0, "van_genuchten_n", "dimensionless", + "registry", "dataset_mean_properties"); } { const vereecken1989_detailed_ptf_result result = calc_ptf_vereecken1989_detailed( 0.25, 0.88, 13.53, 21.15, 16.3, 24.83, 7.0, 5.15, 10.93, 0.07, 4.07, 1.03, 1.466); - assert_close_enough(result.theta_r, 0.08277820000000001, 0.000000000001, 0.000000000001); - assert_close_enough(result.theta_s, 0.4060520000000001, 0.000000000001, 0.000000000001); - assert_close_enough(result.alpha, 0.02522115884641546, 0.000000000001, 0.000000000001); - assert_close_enough(result.n, 0.517175605329397, 0.000000000001, 0.000000000001); + assert_close(result.theta_r, 0.08277820000000001, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "dataset_mean_properties"); + assert_close(result.theta_s, 0.4060520000000001, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "dataset_mean_properties"); + assert_close(result.alpha, 0.02522115884641546, 0.00001, 0.0, "van_genuchten_alpha", + "inverse_centimeter", "registry", "dataset_mean_properties"); + assert_close(result.n, 0.517175605329397, 0.001, 0.0, "van_genuchten_n", "dimensionless", + "registry", "dataset_mean_properties"); } return 0; } diff --git a/targets/ptfkit-native/tests/c/wang2012.c b/targets/ptfkit-native/tests/c/wang2012.c index 1831903..f46bf38 100644 --- a/targets/ptfkit-native/tests/c/wang2012.c +++ b/targets/ptfkit-native/tests/c/wang2012.c @@ -1,14 +1,18 @@ /* @generated by ptfkit-codegen; DO NOT EDIT. */ #include -#include "close_enough.h" +#include "support/close_enough.h" int main() { { const wang2012_ptf_result result = calc_ptf_wang2012(85.0, 10.0, 5.0, 1.22, 0.033, 1193.0); - assert_close_enough(result.theta_s, 0.61540575, 0.000000000001, 0.000001); - assert_close_enough(result.theta_fc, 0.38491949, 0.000000000001, 0.000001); - assert_close_enough(result.k_sat, 0.00003872974, 0.000000000001, 0.000001); + assert_close(result.theta_s, 0.61540575, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "derivation_minimum_soc_case"); + assert_close(result.theta_fc, 0.38491949, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "derivation_minimum_soc_case"); + assert_close(result.k_sat, 0.00003872974, 0.0000000001, 0.01, + "saturated_hydraulic_conductivity", "meter_per_second", "registry", + "derivation_minimum_soc_case"); } return 0; } diff --git a/targets/ptfkit-native/tests/c/weber2020.c b/targets/ptfkit-native/tests/c/weber2020.c index f936d82..5cb8f89 100644 --- a/targets/ptfkit-native/tests/c/weber2020.c +++ b/targets/ptfkit-native/tests/c/weber2020.c @@ -1,18 +1,27 @@ /* @generated by ptfkit-codegen; DO NOT EDIT. */ #include -#include "close_enough.h" +#include "support/close_enough.h" int main() { { const weber2020_ptf_result result = calc_ptf_weber2020(0.05, 0.45, 0.02, 1.6, -0.5, 100.0); - assert_close_enough(result.theta_snc_bw, 0.06267, 0.000000000001, 0.000000000001); - assert_close_enough(result.theta_sc_bw, 0.38607, 0.000000000001, 0.000000000001); - assert_close_enough(result.alpha_bw, 0.0201472407335197, 0.000000000001, 0.000000000001); - assert_close_enough(result.n_bw, 1.71980542683289, 0.000000000001, 0.000000000001); - assert_close_enough(result.tau_bw, -0.887, 0.000000000001, 0.000000000001); - assert_close_enough(result.k_sc_bw, 172.186857498601, 0.000000000001, 0.000000000001); - assert_close_enough(result.k_snc_bw, 0.0190546071796325, 0.000000000001, 0.000000000001); + assert_close(result.theta_snc_bw, 0.06267, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "representative_vgm_parameters"); + assert_close(result.theta_sc_bw, 0.38607, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "representative_vgm_parameters"); + assert_close(result.alpha_bw, 0.0201472407335197, 0.00001, 0.0, "van_genuchten_alpha", + "inverse_centimeter", "registry", "representative_vgm_parameters"); + assert_close(result.n_bw, 1.71980542683289, 0.001, 0.0, "brunswick_n", "dimensionless", + "registry", "representative_vgm_parameters"); + assert_close(result.tau_bw, -0.887, 0.001, 0.0, "brunswick_tau", "dimensionless", + "registry", "representative_vgm_parameters"); + assert_close(result.k_sc_bw, 172.186857498601, 0.0001, 0.01, + "saturated_hydraulic_conductivity", "centimeter_per_day", "registry", + "representative_vgm_parameters"); + assert_close(result.k_snc_bw, 0.0190546071796325, 0.0001, 0.01, + "saturated_hydraulic_conductivity", "centimeter_per_day", "registry", + "representative_vgm_parameters"); } return 0; } diff --git a/targets/ptfkit-native/tests/cpp/ahuja1984.cpp b/targets/ptfkit-native/tests/cpp/ahuja1984.cpp index 90233bc..9465f67 100644 --- a/targets/ptfkit-native/tests/cpp/ahuja1984.cpp +++ b/targets/ptfkit-native/tests/cpp/ahuja1984.cpp @@ -6,16 +6,18 @@ import ptfkit; import ptfkit.ahuja1984; #endif -#include "close_enough.h" +#include "support/close_enough.h" int main() { { const auto result = ptfkit::ahuja1984::calc_ptf_ahuja1984(0.45, 0.25, 100.0, 4.0); - assert_close_enough(result, 0.16, 0.00000000000001, 0.000000000001); + assert_close(result, 0.16, 0.00001, 0.01, "saturated_hydraulic_conductivity", + "centimeter_per_hour", "registry", "exponent_four"); } { const auto result = ptfkit::ahuja1984::calc_ptf_ahuja1984(0.5, 0.2, 100.0, 5.0); - assert_close_enough(result, 0.243, 0.00000000000001, 0.000000000001); + assert_close(result, 0.243, 0.00001, 0.01, "saturated_hydraulic_conductivity", + "centimeter_per_hour", "registry", "exponent_five"); } return 0; } diff --git a/targets/ptfkit-native/tests/cpp/aimrun2009.cpp b/targets/ptfkit-native/tests/cpp/aimrun2009.cpp index 7ea96d8..01362b4 100644 --- a/targets/ptfkit-native/tests/cpp/aimrun2009.cpp +++ b/targets/ptfkit-native/tests/cpp/aimrun2009.cpp @@ -6,24 +6,32 @@ import ptfkit; import ptfkit.aimrun2009; #endif -#include "close_enough.h" +#include "support/close_enough.h" int main() { { const auto result = ptfkit::aimrun2009::calc_ptf_aimrun2009(43.88, 0.94, 12.07, 0.01); - assert_close_enough(result, 0.00000007358406556179513, 0.000000000001, 0.00000001); + assert_close(result, 0.00000007358406556179513, 0.0000000001, 0.01, + "saturated_hydraulic_conductivity", "meter_per_second", "registry", + "mean_topsoil_layer"); } { const auto result = ptfkit::aimrun2009::calc_ptf_aimrun2009(50.21, 1.19, 8.55, 0.007); - assert_close_enough(result, 0.0000000307872446717209, 0.000000000001, 0.00000001); + assert_close(result, 0.0000000307872446717209, 0.0000000001, 0.01, + "saturated_hydraulic_conductivity", "meter_per_second", "registry", + "mean_hardpan_layer"); } { const auto result = ptfkit::aimrun2009::calc_ptf_aimrun2009(58.81, 1.13, 5.12, 0.005); - assert_close_enough(result, 0.000000023343051908963327, 0.000000000001, 0.00000001); + assert_close(result, 0.000000023343051908963327, 0.0000000001, 0.01, + "saturated_hydraulic_conductivity", "meter_per_second", "registry", + "mean_subsoil_layer"); } { const auto result = ptfkit::aimrun2009::calc_ptf_aimrun2009(47.5, 1.08, 1.43, 0.008); - assert_close_enough(result, 0.00000003831168764444974, 0.000000000001, 0.00000001); + assert_close(result, 0.00000003831168764444974, 0.0000000001, 0.01, + "saturated_hydraulic_conductivity", "meter_per_second", "registry", + "min_organic_matter"); } return 0; } diff --git a/targets/ptfkit-native/tests/cpp/beniaich2023.cpp b/targets/ptfkit-native/tests/cpp/beniaich2023.cpp index 2e4020b..7dfe7a6 100644 --- a/targets/ptfkit-native/tests/cpp/beniaich2023.cpp +++ b/targets/ptfkit-native/tests/cpp/beniaich2023.cpp @@ -6,7 +6,7 @@ import ptfkit; import ptfkit.beniaich2023; #endif -#include "close_enough.h" +#include "support/close_enough.h" #include int main() { @@ -14,89 +14,122 @@ int main() { const auto result = ptfkit::beniaich2023::calc_ptf_beniaich2023_slr1(20.0); static_assert(std::is_same_v, ptfkit::beniaich2023::Beniaich2023PTFResult>); - assert_close_enough(result.water_saturation, 0.57427, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_field_capacity, 0.17577, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_wilting_point, 0.09621, 0.000000000001, 0.000000000001); + assert_close(result.water_saturation, 0.57427, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_field_capacity, 0.17577, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_wilting_point, 0.09621, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); } { const auto result = ptfkit::beniaich2023::calc_ptf_beniaich2023_slr2(30.0); static_assert(std::is_same_v, ptfkit::beniaich2023::Beniaich2023PTFResult>); - assert_close_enough(result.water_saturation, 0.68478, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_field_capacity, 0.24878, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_wilting_point, 0.16131, 0.000000000001, 0.000000000001); + assert_close(result.water_saturation, 0.68478, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_field_capacity, 0.24878, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_wilting_point, 0.16131, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); } { const auto result = ptfkit::beniaich2023::calc_ptf_beniaich2023_slr3(50.0); static_assert(std::is_same_v, ptfkit::beniaich2023::Beniaich2023PTFResult>); - assert_close_enough(result.water_saturation, 0.6007, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_field_capacity, 0.1848, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_wilting_point, 0.11077, 0.000000000001, 0.000000000001); + assert_close(result.water_saturation, 0.6007, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_field_capacity, 0.1848, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_wilting_point, 0.11077, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); } { const auto result = ptfkit::beniaich2023::calc_ptf_beniaich2023_slr4(20.0, 30.0); static_assert(std::is_same_v, ptfkit::beniaich2023::Beniaich2023PTFResult>); - assert_close_enough(result.water_saturation, 0.74501, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_field_capacity, 0.30678, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_wilting_point, 0.19915, 0.000000000001, 0.000000000001); + assert_close(result.water_saturation, 0.74501, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_field_capacity, 0.30678, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_wilting_point, 0.19915, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); } { const auto result = ptfkit::beniaich2023::calc_ptf_beniaich2023_slr5(20.0, 40.0); static_assert(std::is_same_v, ptfkit::beniaich2023::Beniaich2023PTFResult>); - assert_close_enough(result.water_saturation, 0.68578, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_field_capacity, 0.241875, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_wilting_point, 0.16176, 0.000000000001, 0.000000000001); + assert_close(result.water_saturation, 0.68578, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_field_capacity, 0.241875, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_wilting_point, 0.16176, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); } { const auto result = ptfkit::beniaich2023::calc_ptf_beniaich2023_slr6(2.0); static_assert(std::is_same_v, ptfkit::beniaich2023::Beniaich2023PTFResult>); - assert_close_enough(result.water_saturation, 0.66749, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_field_capacity, 0.24009, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_wilting_point, 0.15562, 0.000000000001, 0.000000000001); + assert_close(result.water_saturation, 0.66749, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_field_capacity, 0.24009, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_wilting_point, 0.15562, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); } { const auto result = ptfkit::beniaich2023::calc_ptf_beniaich2023_mlr1(30.0, 50.0, 2.0); static_assert(std::is_same_v, ptfkit::beniaich2023::Beniaich2023PTFResult>); - assert_close_enough(result.water_saturation, 0.56266, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_field_capacity, 0.17238, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_wilting_point, 0.09366, 0.000000000001, 0.000000000001); + assert_close(result.water_saturation, 0.56266, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_field_capacity, 0.17238, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_wilting_point, 0.09366, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); } { const auto result = ptfkit::beniaich2023::calc_ptf_beniaich2023_mlr2(50.0, 2.0); static_assert(std::is_same_v, ptfkit::beniaich2023::Beniaich2023PTFResult>); - assert_close_enough(result.water_saturation, 0.58954, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_field_capacity, 0.18025, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_wilting_point, 0.10825, 0.000000000001, 0.000000000001); + assert_close(result.water_saturation, 0.58954, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_field_capacity, 0.18025, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_wilting_point, 0.10825, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); } { const auto result = ptfkit::beniaich2023::calc_ptf_beniaich2023_mlr3(30.0, 2.0); static_assert(std::is_same_v, ptfkit::beniaich2023::Beniaich2023PTFResult>); - assert_close_enough(result.water_saturation, 0.67031, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_field_capacity, 0.24275, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_wilting_point, 0.15755, 0.000000000001, 0.000000000001); + assert_close(result.water_saturation, 0.67031, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_field_capacity, 0.24275, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_wilting_point, 0.15755, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); } { const auto result = ptfkit::beniaich2023::calc_ptf_beniaich2023_mlr4(20.0, 2.0); static_assert(std::is_same_v, ptfkit::beniaich2023::Beniaich2023PTFResult>); - assert_close_enough(result.water_saturation, 0.5589, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_field_capacity, 0.16859, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_wilting_point, 0.09157, 0.000000000001, 0.000000000001); + assert_close(result.water_saturation, 0.5589, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_field_capacity, 0.16859, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_wilting_point, 0.09157, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); } { const auto result = ptfkit::beniaich2023::calc_ptf_beniaich2023_mlr5(20.0, 30.0, 2.0); static_assert(std::is_same_v, ptfkit::beniaich2023::Beniaich2023PTFResult>); - assert_close_enough(result.water_saturation, 0.56229, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_field_capacity, 0.172, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_wilting_point, 0.09379, 0.000000000001, 0.000000000001); + assert_close(result.water_saturation, 0.56229, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_field_capacity, 0.172, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_wilting_point, 0.09379, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); } return 0; } diff --git a/targets/ptfkit-native/tests/cpp/chakraborty2011.cpp b/targets/ptfkit-native/tests/cpp/chakraborty2011.cpp index 871dc2d..b027703 100644 --- a/targets/ptfkit-native/tests/cpp/chakraborty2011.cpp +++ b/targets/ptfkit-native/tests/cpp/chakraborty2011.cpp @@ -6,7 +6,7 @@ import ptfkit; import ptfkit.chakraborty2011; #endif -#include "close_enough.h" +#include "support/close_enough.h" #include int main() { @@ -14,57 +14,81 @@ int main() { const auto result = ptfkit::chakraborty2011::calc_ptf_chakraborty2011_eq1(20.0, 30.0); static_assert(std::is_same_v, ptfkit::chakraborty2011::Chakraborty2011PTFResult>); - assert_close_enough(result.water_content_33, 0.2488, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_content_100, 0.19758, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_content_500, 0.13528, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_content_1500, 0.11702, 0.000000000001, 0.000000000001); + assert_close(result.water_content_33, 0.2488, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_content_100, 0.19758, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_content_500, 0.13528, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_content_1500, 0.11702, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); } { const auto result = ptfkit::chakraborty2011::calc_ptf_chakraborty2011_eq2(50.0, 1.5); static_assert(std::is_same_v, ptfkit::chakraborty2011::Chakraborty2011PTFResult>); - assert_close_enough(result.water_content_33, 0.219415, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_content_100, 0.175765, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_content_500, 0.141025, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_content_1500, 0.118315, 0.000000000001, 0.000000000001); + assert_close(result.water_content_33, 0.219415, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_content_100, 0.175765, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_content_500, 0.141025, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_content_1500, 0.118315, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); } { const auto result = ptfkit::chakraborty2011::calc_ptf_chakraborty2011_eq3(20.0, 30.0, 1.5); static_assert(std::is_same_v, ptfkit::chakraborty2011::Chakraborty2011PTFResult>); - assert_close_enough(result.water_content_33, 0.25776, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_content_100, 0.201085, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_content_500, 0.139175, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_content_1500, 0.117865, 0.000000000001, 0.000000000001); + assert_close(result.water_content_33, 0.25776, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_content_100, 0.201085, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_content_500, 0.139175, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_content_1500, 0.117865, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); } { const auto result = ptfkit::chakraborty2011::calc_ptf_chakraborty2011_eq4(20.0, 30.0, 50.0); static_assert(std::is_same_v, ptfkit::chakraborty2011::Chakraborty2011PTFResult>); - assert_close_enough(result.water_content_33, 0.24397, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_content_100, 0.19235, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_content_500, 0.13116, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_content_1500, 0.11325, 0.000000000001, 0.000000000001); + assert_close(result.water_content_33, 0.24397, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_content_100, 0.19235, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_content_500, 0.13116, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_content_1500, 0.11325, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); } { const auto result = ptfkit::chakraborty2011::calc_ptf_chakraborty2011_eq5(20.0, 30.0, 50.0, 1.5); static_assert(std::is_same_v, ptfkit::chakraborty2011::Chakraborty2011PTFResult>); - assert_close_enough(result.water_content_33, 0.249785, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_content_100, 0.19166, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_content_500, 0.131065, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_content_1500, 0.110595, 0.000000000001, 0.000000000001); + assert_close(result.water_content_33, 0.249785, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_content_100, 0.19166, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_content_500, 0.131065, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_content_1500, 0.110595, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); } { const auto result = ptfkit::chakraborty2011::calc_ptf_chakraborty2011_eq6(20.0, 30.0, 50.0, 0.5, 1.5); static_assert(std::is_same_v, ptfkit::chakraborty2011::Chakraborty2011PTFResult>); - assert_close_enough(result.water_content_33, 0.25207, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_content_100, 0.19695, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_content_500, 0.13364, 0.000000000001, 0.000000000001); - assert_close_enough(result.water_content_1500, 0.113175, 0.000000000001, 0.000000000001); + assert_close(result.water_content_33, 0.25207, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_content_100, 0.19695, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_content_500, 0.13364, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); + assert_close(result.water_content_1500, 0.113175, 0.001, 0.0, "gravimetric_water_content", + "mass_fraction", "registry", "representative_case"); } return 0; } diff --git a/targets/ptfkit-native/tests/cpp/clapp1978.cpp b/targets/ptfkit-native/tests/cpp/clapp1978.cpp index be33e06..af6f2af 100644 --- a/targets/ptfkit-native/tests/cpp/clapp1978.cpp +++ b/targets/ptfkit-native/tests/cpp/clapp1978.cpp @@ -6,7 +6,7 @@ import ptfkit; import ptfkit.clapp1978; #endif -#include "close_enough.h" +#include "support/close_enough.h" #include int main() { @@ -15,132 +15,209 @@ int main() { ptfkit::clapp1978::calc_ptf_clapp1978(ptfkit::clapp1978::UsdaTextureClass::Sand); static_assert(std::is_same_v, ptfkit::clapp1978::Clapp1978Parameters>); - assert_close_enough(result.b, 4.05, 0.0, 0.0); - assert_close_enough(result.saturation_suction, 3.5, 0.0, 0.0); - assert_close_enough(result.wetting_front_suction, 4.66, 0.0, 0.0); - assert_close_enough(result.saturated_water_content, 0.395, 0.0, 0.0); - assert_close_enough(result.saturated_hydraulic_conductivity, 1.056, 0.0, 0.0); - assert_close_enough(result.sorptivity, 1.52, 0.0, 0.0); + assert_close(result.b, 4.05, 0.001, 0.0, "campbell_b", "dimensionless", "registry", + "table_2_sand"); + assert_close(result.saturation_suction, 3.5, 0.01, 0.0, "matric_potential", + "centimeter_water_head", "registry", "table_2_sand"); + assert_close(result.wetting_front_suction, 4.66, 0.01, 0.0, "matric_potential", + "centimeter_water_head", "registry", "table_2_sand"); + assert_close(result.saturated_water_content, 0.395, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "table_2_sand"); + assert_close(result.saturated_hydraulic_conductivity, 1.056, 0.00001, 0.01, + "saturated_hydraulic_conductivity", "centimeter_per_minute", "registry", + "table_2_sand"); + assert_close(result.sorptivity, 1.52, 0.0001, 0.01, "sorptivity", + "centimeter_per_square_root_minute", "registry", "table_2_sand"); } { const auto result = ptfkit::clapp1978::calc_ptf_clapp1978(ptfkit::clapp1978::UsdaTextureClass::LoamySand); static_assert(std::is_same_v, ptfkit::clapp1978::Clapp1978Parameters>); - assert_close_enough(result.b, 4.38, 0.0, 0.0); - assert_close_enough(result.saturation_suction, 1.78, 0.0, 0.0); - assert_close_enough(result.wetting_front_suction, 2.38, 0.0, 0.0); - assert_close_enough(result.saturated_water_content, 0.41, 0.0, 0.0); - assert_close_enough(result.saturated_hydraulic_conductivity, 0.938, 0.0, 0.0); - assert_close_enough(result.sorptivity, 1.04, 0.0, 0.0); + assert_close(result.b, 4.38, 0.001, 0.0, "campbell_b", "dimensionless", "registry", + "table_2_loamy_sand"); + assert_close(result.saturation_suction, 1.78, 0.01, 0.0, "matric_potential", + "centimeter_water_head", "registry", "table_2_loamy_sand"); + assert_close(result.wetting_front_suction, 2.38, 0.01, 0.0, "matric_potential", + "centimeter_water_head", "registry", "table_2_loamy_sand"); + assert_close(result.saturated_water_content, 0.41, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "table_2_loamy_sand"); + assert_close(result.saturated_hydraulic_conductivity, 0.938, 0.00001, 0.01, + "saturated_hydraulic_conductivity", "centimeter_per_minute", "registry", + "table_2_loamy_sand"); + assert_close(result.sorptivity, 1.04, 0.0001, 0.01, "sorptivity", + "centimeter_per_square_root_minute", "registry", "table_2_loamy_sand"); } { const auto result = ptfkit::clapp1978::calc_ptf_clapp1978(ptfkit::clapp1978::UsdaTextureClass::SandyLoam); static_assert(std::is_same_v, ptfkit::clapp1978::Clapp1978Parameters>); - assert_close_enough(result.b, 4.9, 0.0, 0.0); - assert_close_enough(result.saturation_suction, 7.18, 0.0, 0.0); - assert_close_enough(result.wetting_front_suction, 9.52, 0.0, 0.0); - assert_close_enough(result.saturated_water_content, 0.435, 0.0, 0.0); - assert_close_enough(result.saturated_hydraulic_conductivity, 0.208, 0.0, 0.0); - assert_close_enough(result.sorptivity, 1.03, 0.0, 0.0); + assert_close(result.b, 4.9, 0.001, 0.0, "campbell_b", "dimensionless", "registry", + "table_2_sandy_loam"); + assert_close(result.saturation_suction, 7.18, 0.01, 0.0, "matric_potential", + "centimeter_water_head", "registry", "table_2_sandy_loam"); + assert_close(result.wetting_front_suction, 9.52, 0.01, 0.0, "matric_potential", + "centimeter_water_head", "registry", "table_2_sandy_loam"); + assert_close(result.saturated_water_content, 0.435, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "table_2_sandy_loam"); + assert_close(result.saturated_hydraulic_conductivity, 0.208, 0.00001, 0.01, + "saturated_hydraulic_conductivity", "centimeter_per_minute", "registry", + "table_2_sandy_loam"); + assert_close(result.sorptivity, 1.03, 0.0001, 0.01, "sorptivity", + "centimeter_per_square_root_minute", "registry", "table_2_sandy_loam"); } { const auto result = ptfkit::clapp1978::calc_ptf_clapp1978(ptfkit::clapp1978::UsdaTextureClass::SiltLoam); static_assert(std::is_same_v, ptfkit::clapp1978::Clapp1978Parameters>); - assert_close_enough(result.b, 5.3, 0.0, 0.0); - assert_close_enough(result.saturation_suction, 56.6, 0.0, 0.0); - assert_close_enough(result.wetting_front_suction, 75.3, 0.0, 0.0); - assert_close_enough(result.saturated_water_content, 0.485, 0.0, 0.0); - assert_close_enough(result.saturated_hydraulic_conductivity, 0.0432, 0.0, 0.0); - assert_close_enough(result.sorptivity, 1.26, 0.0, 0.0); + assert_close(result.b, 5.3, 0.001, 0.0, "campbell_b", "dimensionless", "registry", + "table_2_silt_loam"); + assert_close(result.saturation_suction, 56.6, 0.01, 0.0, "matric_potential", + "centimeter_water_head", "registry", "table_2_silt_loam"); + assert_close(result.wetting_front_suction, 75.3, 0.01, 0.0, "matric_potential", + "centimeter_water_head", "registry", "table_2_silt_loam"); + assert_close(result.saturated_water_content, 0.485, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "table_2_silt_loam"); + assert_close(result.saturated_hydraulic_conductivity, 0.0432, 0.00001, 0.01, + "saturated_hydraulic_conductivity", "centimeter_per_minute", "registry", + "table_2_silt_loam"); + assert_close(result.sorptivity, 1.26, 0.0001, 0.01, "sorptivity", + "centimeter_per_square_root_minute", "registry", "table_2_silt_loam"); } { const auto result = ptfkit::clapp1978::calc_ptf_clapp1978(ptfkit::clapp1978::UsdaTextureClass::Loam); static_assert(std::is_same_v, ptfkit::clapp1978::Clapp1978Parameters>); - assert_close_enough(result.b, 5.39, 0.0, 0.0); - assert_close_enough(result.saturation_suction, 14.6, 0.0, 0.0); - assert_close_enough(result.wetting_front_suction, 20.0, 0.0, 0.0); - assert_close_enough(result.saturated_water_content, 0.451, 0.0, 0.0); - assert_close_enough(result.saturated_hydraulic_conductivity, 0.0417, 0.0, 0.0); - assert_close_enough(result.sorptivity, 0.693, 0.0, 0.0); + assert_close(result.b, 5.39, 0.001, 0.0, "campbell_b", "dimensionless", "registry", + "table_2_loam"); + assert_close(result.saturation_suction, 14.6, 0.01, 0.0, "matric_potential", + "centimeter_water_head", "registry", "table_2_loam"); + assert_close(result.wetting_front_suction, 20.0, 0.01, 0.0, "matric_potential", + "centimeter_water_head", "registry", "table_2_loam"); + assert_close(result.saturated_water_content, 0.451, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "table_2_loam"); + assert_close(result.saturated_hydraulic_conductivity, 0.0417, 0.00001, 0.01, + "saturated_hydraulic_conductivity", "centimeter_per_minute", "registry", + "table_2_loam"); + assert_close(result.sorptivity, 0.693, 0.0001, 0.01, "sorptivity", + "centimeter_per_square_root_minute", "registry", "table_2_loam"); } { const auto result = ptfkit::clapp1978::calc_ptf_clapp1978( ptfkit::clapp1978::UsdaTextureClass::SandyClayLoam); static_assert(std::is_same_v, ptfkit::clapp1978::Clapp1978Parameters>); - assert_close_enough(result.b, 7.12, 0.0, 0.0); - assert_close_enough(result.saturation_suction, 8.63, 0.0, 0.0); - assert_close_enough(result.wetting_front_suction, 11.7, 0.0, 0.0); - assert_close_enough(result.saturated_water_content, 0.42, 0.0, 0.0); - assert_close_enough(result.saturated_hydraulic_conductivity, 0.0378, 0.0, 0.0); - assert_close_enough(result.sorptivity, 0.488, 0.0, 0.0); + assert_close(result.b, 7.12, 0.001, 0.0, "campbell_b", "dimensionless", "registry", + "table_2_sandy_clay_loam"); + assert_close(result.saturation_suction, 8.63, 0.01, 0.0, "matric_potential", + "centimeter_water_head", "registry", "table_2_sandy_clay_loam"); + assert_close(result.wetting_front_suction, 11.7, 0.01, 0.0, "matric_potential", + "centimeter_water_head", "registry", "table_2_sandy_clay_loam"); + assert_close(result.saturated_water_content, 0.42, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "table_2_sandy_clay_loam"); + assert_close(result.saturated_hydraulic_conductivity, 0.0378, 0.00001, 0.01, + "saturated_hydraulic_conductivity", "centimeter_per_minute", "registry", + "table_2_sandy_clay_loam"); + assert_close(result.sorptivity, 0.488, 0.0001, 0.01, "sorptivity", + "centimeter_per_square_root_minute", "registry", "table_2_sandy_clay_loam"); } { const auto result = ptfkit::clapp1978::calc_ptf_clapp1978( ptfkit::clapp1978::UsdaTextureClass::SiltyClayLoam); static_assert(std::is_same_v, ptfkit::clapp1978::Clapp1978Parameters>); - assert_close_enough(result.b, 7.75, 0.0, 0.0); - assert_close_enough(result.saturation_suction, 14.6, 0.0, 0.0); - assert_close_enough(result.wetting_front_suction, 19.7, 0.0, 0.0); - assert_close_enough(result.saturated_water_content, 0.477, 0.0, 0.0); - assert_close_enough(result.saturated_hydraulic_conductivity, 0.0102, 0.0, 0.0); - assert_close_enough(result.sorptivity, 0.31, 0.0, 0.0); + assert_close(result.b, 7.75, 0.001, 0.0, "campbell_b", "dimensionless", "registry", + "table_2_silty_clay_loam"); + assert_close(result.saturation_suction, 14.6, 0.01, 0.0, "matric_potential", + "centimeter_water_head", "registry", "table_2_silty_clay_loam"); + assert_close(result.wetting_front_suction, 19.7, 0.01, 0.0, "matric_potential", + "centimeter_water_head", "registry", "table_2_silty_clay_loam"); + assert_close(result.saturated_water_content, 0.477, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "table_2_silty_clay_loam"); + assert_close(result.saturated_hydraulic_conductivity, 0.0102, 0.00001, 0.01, + "saturated_hydraulic_conductivity", "centimeter_per_minute", "registry", + "table_2_silty_clay_loam"); + assert_close(result.sorptivity, 0.31, 0.0001, 0.01, "sorptivity", + "centimeter_per_square_root_minute", "registry", "table_2_silty_clay_loam"); } { const auto result = ptfkit::clapp1978::calc_ptf_clapp1978(ptfkit::clapp1978::UsdaTextureClass::ClayLoam); static_assert(std::is_same_v, ptfkit::clapp1978::Clapp1978Parameters>); - assert_close_enough(result.b, 8.52, 0.0, 0.0); - assert_close_enough(result.saturation_suction, 36.1, 0.0, 0.0); - assert_close_enough(result.wetting_front_suction, 48.1, 0.0, 0.0); - assert_close_enough(result.saturated_water_content, 0.476, 0.0, 0.0); - assert_close_enough(result.saturated_hydraulic_conductivity, 0.0147, 0.0, 0.0); - assert_close_enough(result.sorptivity, 0.537, 0.0, 0.0); + assert_close(result.b, 8.52, 0.001, 0.0, "campbell_b", "dimensionless", "registry", + "table_2_clay_loam"); + assert_close(result.saturation_suction, 36.1, 0.01, 0.0, "matric_potential", + "centimeter_water_head", "registry", "table_2_clay_loam"); + assert_close(result.wetting_front_suction, 48.1, 0.01, 0.0, "matric_potential", + "centimeter_water_head", "registry", "table_2_clay_loam"); + assert_close(result.saturated_water_content, 0.476, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "table_2_clay_loam"); + assert_close(result.saturated_hydraulic_conductivity, 0.0147, 0.00001, 0.01, + "saturated_hydraulic_conductivity", "centimeter_per_minute", "registry", + "table_2_clay_loam"); + assert_close(result.sorptivity, 0.537, 0.0001, 0.01, "sorptivity", + "centimeter_per_square_root_minute", "registry", "table_2_clay_loam"); } { const auto result = ptfkit::clapp1978::calc_ptf_clapp1978(ptfkit::clapp1978::UsdaTextureClass::SandyClay); static_assert(std::is_same_v, ptfkit::clapp1978::Clapp1978Parameters>); - assert_close_enough(result.b, 10.4, 0.0, 0.0); - assert_close_enough(result.saturation_suction, 6.16, 0.0, 0.0); - assert_close_enough(result.wetting_front_suction, 8.18, 0.0, 0.0); - assert_close_enough(result.saturated_water_content, 0.426, 0.0, 0.0); - assert_close_enough(result.saturated_hydraulic_conductivity, 0.013, 0.0, 0.0); - assert_close_enough(result.sorptivity, 0.223, 0.0, 0.0); + assert_close(result.b, 10.4, 0.001, 0.0, "campbell_b", "dimensionless", "registry", + "table_2_sandy_clay"); + assert_close(result.saturation_suction, 6.16, 0.01, 0.0, "matric_potential", + "centimeter_water_head", "registry", "table_2_sandy_clay"); + assert_close(result.wetting_front_suction, 8.18, 0.01, 0.0, "matric_potential", + "centimeter_water_head", "registry", "table_2_sandy_clay"); + assert_close(result.saturated_water_content, 0.426, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "table_2_sandy_clay"); + assert_close(result.saturated_hydraulic_conductivity, 0.013, 0.00001, 0.01, + "saturated_hydraulic_conductivity", "centimeter_per_minute", "registry", + "table_2_sandy_clay"); + assert_close(result.sorptivity, 0.223, 0.0001, 0.01, "sorptivity", + "centimeter_per_square_root_minute", "registry", "table_2_sandy_clay"); } { const auto result = ptfkit::clapp1978::calc_ptf_clapp1978(ptfkit::clapp1978::UsdaTextureClass::SiltyClay); static_assert(std::is_same_v, ptfkit::clapp1978::Clapp1978Parameters>); - assert_close_enough(result.b, 10.4, 0.0, 0.0); - assert_close_enough(result.saturation_suction, 17.4, 0.0, 0.0); - assert_close_enough(result.wetting_front_suction, 23.0, 0.0, 0.0); - assert_close_enough(result.saturated_water_content, 0.492, 0.0, 0.0); - assert_close_enough(result.saturated_hydraulic_conductivity, 0.0062, 0.0, 0.0); - assert_close_enough(result.sorptivity, 0.242, 0.0, 0.0); + assert_close(result.b, 10.4, 0.001, 0.0, "campbell_b", "dimensionless", "registry", + "table_2_silty_clay"); + assert_close(result.saturation_suction, 17.4, 0.01, 0.0, "matric_potential", + "centimeter_water_head", "registry", "table_2_silty_clay"); + assert_close(result.wetting_front_suction, 23.0, 0.01, 0.0, "matric_potential", + "centimeter_water_head", "registry", "table_2_silty_clay"); + assert_close(result.saturated_water_content, 0.492, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "table_2_silty_clay"); + assert_close(result.saturated_hydraulic_conductivity, 0.0062, 0.00001, 0.01, + "saturated_hydraulic_conductivity", "centimeter_per_minute", "registry", + "table_2_silty_clay"); + assert_close(result.sorptivity, 0.242, 0.0001, 0.01, "sorptivity", + "centimeter_per_square_root_minute", "registry", "table_2_silty_clay"); } { const auto result = ptfkit::clapp1978::calc_ptf_clapp1978(ptfkit::clapp1978::UsdaTextureClass::Clay); static_assert(std::is_same_v, ptfkit::clapp1978::Clapp1978Parameters>); - assert_close_enough(result.b, 11.4, 0.0, 0.0); - assert_close_enough(result.saturation_suction, 18.6, 0.0, 0.0); - assert_close_enough(result.wetting_front_suction, 24.3, 0.0, 0.0); - assert_close_enough(result.saturated_water_content, 0.482, 0.0, 0.0); - assert_close_enough(result.saturated_hydraulic_conductivity, 0.0077, 0.0, 0.0); - assert_close_enough(result.sorptivity, 0.268, 0.0, 0.0); + assert_close(result.b, 11.4, 0.001, 0.0, "campbell_b", "dimensionless", "registry", + "table_2_clay"); + assert_close(result.saturation_suction, 18.6, 0.01, 0.0, "matric_potential", + "centimeter_water_head", "registry", "table_2_clay"); + assert_close(result.wetting_front_suction, 24.3, 0.01, 0.0, "matric_potential", + "centimeter_water_head", "registry", "table_2_clay"); + assert_close(result.saturated_water_content, 0.482, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "table_2_clay"); + assert_close(result.saturated_hydraulic_conductivity, 0.0077, 0.00001, 0.01, + "saturated_hydraulic_conductivity", "centimeter_per_minute", "registry", + "table_2_clay"); + assert_close(result.sorptivity, 0.268, 0.0001, 0.01, "sorptivity", + "centimeter_per_square_root_minute", "registry", "table_2_clay"); } return 0; } diff --git a/targets/ptfkit-native/tests/cpp/comparator.cpp b/targets/ptfkit-native/tests/cpp/comparator.cpp new file mode 100644 index 0000000..43fe913 --- /dev/null +++ b/targets/ptfkit-native/tests/cpp/comparator.cpp @@ -0,0 +1,22 @@ +/* @generated by ptfkit-codegen; DO NOT EDIT. */ + +#include "support/close_enough.h" + +int main() { + const double absolute = 0.001; + const double relative = 0.01; + const double expected_values[] = {0.0, 2.0, -2.0}; + for (int index = 0; index < 3; ++index) { + const double expected = expected_values[index]; + const double tolerance = resolved_tolerance(expected, absolute, relative); + _close_enough_impl(__FILE__, __LINE__, expected + tolerance * 0.5, expected, absolute, + relative, "test_quantity", "1", "registry", "comparator"); + if (is_close(expected + tolerance * 2.0, expected, absolute, relative)) { + return EXIT_FAILURE; + } + } + if (is_close(NAN, 1.0, absolute, relative)) { + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} diff --git a/targets/ptfkit-native/tests/cpp/cosby1984.cpp b/targets/ptfkit-native/tests/cpp/cosby1984.cpp index e7a475f..e6949f4 100644 --- a/targets/ptfkit-native/tests/cpp/cosby1984.cpp +++ b/targets/ptfkit-native/tests/cpp/cosby1984.cpp @@ -6,7 +6,7 @@ import ptfkit; import ptfkit.cosby1984; #endif -#include "close_enough.h" +#include "support/close_enough.h" #include int main() { @@ -14,25 +14,43 @@ int main() { const auto result = ptfkit::cosby1984::calc_ptf_cosby1984_univariate(50.0, 30.0, 20.0); static_assert(std::is_same_v, ptfkit::cosby1984::Cosby1984UnivariatePTFResult>); - assert_close_enough(result.mean_b, 6.09, 0.000000000001, 0.000000000001); - assert_close_enough(result.mean_log_psi_s, 1.225, 0.000000000001, 0.000000000001); - assert_close_enough(result.mean_log_k_sat, -0.119, 0.000000000001, 0.000000000001); - assert_close_enough(result.mean_theta_s, 42.6, 0.000000000001, 0.000000000001); - assert_close_enough(result.sd_b, 2.34, 0.000000000001, 0.000000000001); - assert_close_enough(result.sd_log_k_sat, 0.5553, 0.000000000001, 0.000000000001); - assert_close_enough(result.sd_theta_s, 6.27, 0.000000000001, 0.000000000001); + assert_close(result.mean_b, 6.09, 0.001, 0.0, "campbell_b", "dimensionless", "registry", + "scalar_mid_texture"); + assert_close(result.mean_log_psi_s, 1.225, 0.001, 0.0, "log_saturation_potential", + "reported_log_value", "registry", "scalar_mid_texture"); + assert_close(result.mean_log_k_sat, -0.119, 0.001, 0.0, + "log_saturated_hydraulic_conductivity", "reported_log_value", "registry", + "scalar_mid_texture"); + assert_close(result.mean_theta_s, 42.6, 0.1, 0.0, "volumetric_water_content", + "volume_percent", "registry", "scalar_mid_texture"); + assert_close(result.sd_b, 2.34, 0.001, 0.0, "campbell_b", "dimensionless", "registry", + "scalar_mid_texture"); + assert_close(result.sd_log_k_sat, 0.5553, 0.001, 0.0, + "log_saturated_hydraulic_conductivity", "reported_log_value", "registry", + "scalar_mid_texture"); + assert_close(result.sd_theta_s, 6.27, 0.1, 0.0, "volumetric_water_content", + "volume_percent", "registry", "scalar_mid_texture"); } { const auto result = ptfkit::cosby1984::calc_ptf_cosby1984_univariate(80.0, 15.0, 5.0); static_assert(std::is_same_v, ptfkit::cosby1984::Cosby1984UnivariatePTFResult>); - assert_close_enough(result.mean_b, 3.705, 0.000000000001, 0.000000000001); - assert_close_enough(result.mean_log_psi_s, 0.832, 0.000000000001, 0.000000000001); - assert_close_enough(result.mean_log_k_sat, 0.34, 0.000000000001, 0.000000000001); - assert_close_enough(result.mean_theta_s, 38.82, 0.000000000001, 0.000000000001); - assert_close_enough(result.sd_b, 1.59, 0.000000000001, 0.000000000001); - assert_close_enough(result.sd_log_k_sat, 0.50715, 0.000000000001, 0.000000000001); - assert_close_enough(result.sd_theta_s, 7.365, 0.000000000001, 0.000000000001); + assert_close(result.mean_b, 3.705, 0.001, 0.0, "campbell_b", "dimensionless", "registry", + "scalar_sandy_texture"); + assert_close(result.mean_log_psi_s, 0.832, 0.001, 0.0, "log_saturation_potential", + "reported_log_value", "registry", "scalar_sandy_texture"); + assert_close(result.mean_log_k_sat, 0.34, 0.001, 0.0, + "log_saturated_hydraulic_conductivity", "reported_log_value", "registry", + "scalar_sandy_texture"); + assert_close(result.mean_theta_s, 38.82, 0.1, 0.0, "volumetric_water_content", + "volume_percent", "registry", "scalar_sandy_texture"); + assert_close(result.sd_b, 1.59, 0.001, 0.0, "campbell_b", "dimensionless", "registry", + "scalar_sandy_texture"); + assert_close(result.sd_log_k_sat, 0.50715, 0.001, 0.0, + "log_saturated_hydraulic_conductivity", "reported_log_value", "registry", + "scalar_sandy_texture"); + assert_close(result.sd_theta_s, 7.365, 0.1, 0.0, "volumetric_water_content", + "volume_percent", "registry", "scalar_sandy_texture"); } return 0; } diff --git a/targets/ptfkit-native/tests/cpp/dharumarajan2019.cpp b/targets/ptfkit-native/tests/cpp/dharumarajan2019.cpp index 3f15fe3..0290f16 100644 --- a/targets/ptfkit-native/tests/cpp/dharumarajan2019.cpp +++ b/targets/ptfkit-native/tests/cpp/dharumarajan2019.cpp @@ -6,7 +6,7 @@ import ptfkit; import ptfkit.dharumarajan2019; #endif -#include "close_enough.h" +#include "support/close_enough.h" #include int main() { @@ -16,17 +16,20 @@ int main() { static_assert( std::is_same_v, ptfkit::dharumarajan2019::Dharumarajan2019WaterRetentionResult>); - assert_close_enough(result.field_capacity, 29.7912, 0.000000000001, 0.000000000001); - assert_close_enough(result.permanent_wilting_point, 18.9276, 0.000000000001, - 0.000000000001); + assert_close(result.field_capacity, 29.7912, 0.1, 0.0, "gravimetric_water_content", + "mass_percent", "registry", "northern_study_mean_case"); + assert_close(result.permanent_wilting_point, 18.9276, 0.1, 0.0, "gravimetric_water_content", + "mass_percent", "registry", "northern_study_mean_case"); } { const auto result = ptfkit::dharumarajan2019::calc_ptf_dharumarajan2019_nkp_clay(43.2); static_assert( std::is_same_v, ptfkit::dharumarajan2019::Dharumarajan2019WaterRetentionResult>); - assert_close_enough(result.field_capacity, 30.2832, 0.000000000001, 0.000000000001); - assert_close_enough(result.permanent_wilting_point, 19.157, 0.000000000001, 0.000000000001); + assert_close(result.field_capacity, 30.2832, 0.1, 0.0, "gravimetric_water_content", + "mass_percent", "registry", "northern_study_mean_clay_case"); + assert_close(result.permanent_wilting_point, 19.157, 0.1, 0.0, "gravimetric_water_content", + "mass_percent", "registry", "northern_study_mean_clay_case"); } { const auto result = @@ -34,22 +37,26 @@ int main() { static_assert( std::is_same_v, ptfkit::dharumarajan2019::Dharumarajan2019WaterRetentionResult>); - assert_close_enough(result.field_capacity, 21.8169, 0.000000000001, 0.000000000001); - assert_close_enough(result.permanent_wilting_point, 11.3054, 0.000000000001, - 0.000000000001); + assert_close(result.field_capacity, 21.8169, 0.1, 0.0, "gravimetric_water_content", + "mass_percent", "registry", "southern_study_mean_case"); + assert_close(result.permanent_wilting_point, 11.3054, 0.1, 0.0, "gravimetric_water_content", + "mass_percent", "registry", "southern_study_mean_case"); } { const auto result = ptfkit::dharumarajan2019::calc_ptf_dharumarajan2019_skp_clay(31.5); static_assert( std::is_same_v, ptfkit::dharumarajan2019::Dharumarajan2019WaterRetentionResult>); - assert_close_enough(result.field_capacity, 22.0255, 0.000000000001, 0.000000000001); - assert_close_enough(result.permanent_wilting_point, 11.503, 0.000000000001, 0.000000000001); + assert_close(result.field_capacity, 22.0255, 0.1, 0.0, "gravimetric_water_content", + "mass_percent", "registry", "southern_study_mean_clay_case"); + assert_close(result.permanent_wilting_point, 11.503, 0.1, 0.0, "gravimetric_water_content", + "mass_percent", "registry", "southern_study_mean_clay_case"); } { const auto result = ptfkit::dharumarajan2019::calc_ptf_dharumarajan2019_infiltration(50.0, 20.0, 30.0); - assert_close_enough(result, 18.45, 0.000000000001, 0.000000000001); + assert_close(result, 18.45, 0.0001, 0.01, "infiltration_rate", "millimeter_per_hour", + "registry", "balanced_texture_case"); } return 0; } diff --git a/targets/ptfkit-native/tests/cpp/ferrerjulia2004.cpp b/targets/ptfkit-native/tests/cpp/ferrerjulia2004.cpp index 4bd2778..fcc1539 100644 --- a/targets/ptfkit-native/tests/cpp/ferrerjulia2004.cpp +++ b/targets/ptfkit-native/tests/cpp/ferrerjulia2004.cpp @@ -6,215 +6,257 @@ import ptfkit; import ptfkit.ferrerjulia2004; #endif -#include "close_enough.h" +#include "support/close_enough.h" int main() { { const auto result = ptfkit::ferrerjulia2004::calc_ptf_ferrerjulia2004_campbell_shiozawa(50.0, 25.0); - assert_close_enough(result, 0.025071690110308933, 0.000000000001, 0.000000000001); + assert_close(result, 0.025071690110308933, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const auto result = ptfkit::ferrerjulia2004::calc_ptf_ferrerjulia2004_saxton(50.0, 25.0); - assert_close_enough(result, 0.00022673430784327228, 0.000000000001, 0.000000000001); + assert_close(result, 0.00022673430784327228, 0.0001, 0.01, + "saturated_hydraulic_conductivity", "millimeter_per_hour", "registry", + "reviewer_reference_vector"); } { const auto result = ptfkit::ferrerjulia2004::calc_ptf_ferrerjulia2004_dane_puckett(25.0); - assert_close_enough(result, 8.302039828385373, 0.000000000001, 0.000000000001); + assert_close(result, 8.302039828385373, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const auto result = ptfkit::ferrerjulia2004::calc_ptf_ferrerjulia2004_puckett(25.0); - assert_close_enough(result, 1.1257967371765654, 0.000000000001, 0.000000000001); + assert_close(result, 1.1257967371765654, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const auto result = ptfkit::ferrerjulia2004::calc_ptf_ferrerjulia2004_cosby(50.0, 25.0); - assert_close_enough(result, 26.091830970918934, 0.000000000001, 0.000000000001); + assert_close(result, 26.091830970918934, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const auto result = ptfkit::ferrerjulia2004::calc_ptf_ferrerjulia2004_humic_acrisol_sand(50.0); - assert_close_enough(result, 14.529879794648146, 0.000000000001, 0.000000000001); + assert_close(result, 14.529879794648146, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const auto result = ptfkit::ferrerjulia2004::calc_ptf_ferrerjulia2004_humic_acrisol_texture_organic_matter( 50.0, 25.0, 2.5); - assert_close_enough(result, 19.563475, 0.000000000001, 0.000000000001); + assert_close(result, 19.563475, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const auto result = ptfkit::ferrerjulia2004::calc_ptf_ferrerjulia2004_calcic_cambisol_sand(50.0); - assert_close_enough(result, 9.995162000935244, 0.000000000001, 0.000000000001); + assert_close(result, 9.995162000935244, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const auto result = ptfkit::ferrerjulia2004:: calc_ptf_ferrerjulia2004_calcic_cambisol_texture_organic_matter(50.0, 25.0, 2.5); - assert_close_enough(result, 16.51625, 0.000000000001, 0.000000000001); + assert_close(result, 16.51625, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const auto result = ptfkit::ferrerjulia2004::calc_ptf_ferrerjulia2004_dystric_cambisol_sand(50.0); - assert_close_enough(result, 13.413573870265868, 0.000000000001, 0.000000000001); + assert_close(result, 13.413573870265868, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const auto result = ptfkit::ferrerjulia2004:: calc_ptf_ferrerjulia2004_dystric_cambisol_texture_organic_matter(50.0, 25.0, 2.5); - assert_close_enough(result, 20.79, 0.000000000001, 0.000000000001); + assert_close(result, 20.79, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const auto result = ptfkit::ferrerjulia2004::calc_ptf_ferrerjulia2004_eutric_cambisol_sand(50.0); - assert_close_enough(result, 11.570199173737363, 0.000000000001, 0.000000000001); + assert_close(result, 11.570199173737363, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const auto result = ptfkit::ferrerjulia2004:: calc_ptf_ferrerjulia2004_eutric_cambisol_texture_organic_matter(50.0, 25.0, 2.5); - assert_close_enough(result, 19.5235, 0.000000000001, 0.000000000001); + assert_close(result, 19.5235, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const auto result = ptfkit::ferrerjulia2004::calc_ptf_ferrerjulia2004_gleyic_cambisol_sand(50.0); - assert_close_enough(result, 6.559110321079926, 0.000000000001, 0.000000000001); + assert_close(result, 6.559110321079926, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const auto result = ptfkit::ferrerjulia2004:: calc_ptf_ferrerjulia2004_gleyic_cambisol_texture_organic_matter(50.0, 25.0, 2.5); - assert_close_enough(result, 11.514, 0.000000000001, 0.000000000001); + assert_close(result, 11.514, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const auto result = ptfkit::ferrerjulia2004::calc_ptf_ferrerjulia2004_humic_cambisol_sand(50.0); - assert_close_enough(result, 13.820129957492508, 0.000000000001, 0.000000000001); + assert_close(result, 13.820129957492508, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const auto result = ptfkit::ferrerjulia2004::calc_ptf_ferrerjulia2004_humic_cambisol_texture_organic_matter( 50.0, 25.0, 2.5); - assert_close_enough(result, 20.9029, 0.000000000001, 0.000000000001); + assert_close(result, 20.9029, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const auto result = ptfkit::ferrerjulia2004::calc_ptf_ferrerjulia2004_calcaric_fluvisol_sand(50.0); - assert_close_enough(result, 10.959482760491376, 0.000000000001, 0.000000000001); + assert_close(result, 10.959482760491376, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const auto result = ptfkit::ferrerjulia2004:: calc_ptf_ferrerjulia2004_calcaric_fluvisol_texture_organic_matter(50.0, 25.0, 2.5); - assert_close_enough(result, 13.101, 0.000000000001, 0.000000000001); + assert_close(result, 13.101, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const auto result = ptfkit::ferrerjulia2004::calc_ptf_ferrerjulia2004_calcic_luvisol_sand(50.0); - assert_close_enough(result, 6.931067612606957, 0.000000000001, 0.000000000001); + assert_close(result, 6.931067612606957, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const auto result = ptfkit::ferrerjulia2004::calc_ptf_ferrerjulia2004_calcic_luvisol_texture_organic_matter( 50.0, 25.0, 2.5); - assert_close_enough(result, 8.9332, 0.000000000001, 0.000000000001); + assert_close(result, 8.9332, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const auto result = ptfkit::ferrerjulia2004::calc_ptf_ferrerjulia2004_chromic_luvisol_sand(50.0); - assert_close_enough(result, 10.61655727277843, 0.000000000001, 0.000000000001); + assert_close(result, 10.61655727277843, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const auto result = ptfkit::ferrerjulia2004:: calc_ptf_ferrerjulia2004_chromic_luvisol_texture_organic_matter(50.0, 25.0, 2.5); - assert_close_enough(result, 17.1465, 0.000000000001, 0.000000000001); + assert_close(result, 17.1465, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const auto result = ptfkit::ferrerjulia2004::calc_ptf_ferrerjulia2004_gleyic_luvisol_sand(50.0); - assert_close_enough(result, 7.049012285884197, 0.000000000001, 0.000000000001); + assert_close(result, 7.049012285884197, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const auto result = ptfkit::ferrerjulia2004::calc_ptf_ferrerjulia2004_gleyic_luvisol_texture_organic_matter( 50.0, 25.0, 2.5); - assert_close_enough(result, 19.55, 0.000000000001, 0.000000000001); + assert_close(result, 19.55, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const auto result = ptfkit::ferrerjulia2004::calc_ptf_ferrerjulia2004_orthic_luvisol_sand(50.0); - assert_close_enough(result, 12.04280911180458, 0.000000000001, 0.000000000001); + assert_close(result, 12.04280911180458, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const auto result = ptfkit::ferrerjulia2004::calc_ptf_ferrerjulia2004_orthic_luvisol_texture_organic_matter( 50.0, 25.0, 2.5); - assert_close_enough(result, 21.40425, 0.000000000001, 0.000000000001); + assert_close(result, 21.40425, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const auto result = ptfkit::ferrerjulia2004::calc_ptf_ferrerjulia2004_ranker_sand(50.0); - assert_close_enough(result, 12.423399456061938, 0.000000000001, 0.000000000001); + assert_close(result, 12.423399456061938, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const auto result = ptfkit::ferrerjulia2004::calc_ptf_ferrerjulia2004_ranker_texture_organic_matter( 50.0, 25.0, 2.5); - assert_close_enough(result, 22.4515, 0.000000000001, 0.000000000001); + assert_close(result, 22.4515, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const auto result = ptfkit::ferrerjulia2004::calc_ptf_ferrerjulia2004_calcaric_regosol_sand(50.0); - assert_close_enough(result, 12.267374733833096, 0.000000000001, 0.000000000001); + assert_close(result, 12.267374733833096, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const auto result = ptfkit::ferrerjulia2004:: calc_ptf_ferrerjulia2004_calcaric_regosol_texture_organic_matter(50.0, 25.0, 2.5); - assert_close_enough(result, 20.532, 0.000000000001, 0.000000000001); + assert_close(result, 20.532, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const auto result = ptfkit::ferrerjulia2004::calc_ptf_ferrerjulia2004_dystric_regosol_sand(50.0); - assert_close_enough(result, 12.081401313183196, 0.000000000001, 0.000000000001); + assert_close(result, 12.081401313183196, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const auto result = ptfkit::ferrerjulia2004:: calc_ptf_ferrerjulia2004_dystric_regosol_texture_organic_matter(50.0, 25.0, 2.5); - assert_close_enough(result, 17.1335, 0.000000000001, 0.000000000001); + assert_close(result, 17.1335, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const auto result = ptfkit::ferrerjulia2004::calc_ptf_ferrerjulia2004_eutric_regosol_sand(50.0); - assert_close_enough(result, 13.674049986082728, 0.000000000001, 0.000000000001); + assert_close(result, 13.674049986082728, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const auto result = ptfkit::ferrerjulia2004::calc_ptf_ferrerjulia2004_eutric_regosol_texture_organic_matter( 50.0, 25.0, 2.5); - assert_close_enough(result, 24.06375, 0.000000000001, 0.000000000001); + assert_close(result, 24.06375, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const auto result = ptfkit::ferrerjulia2004::calc_ptf_ferrerjulia2004_rendzina_sand(50.0); - assert_close_enough(result, 12.515763717130312, 0.000000000001, 0.000000000001); + assert_close(result, 12.515763717130312, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const auto result = ptfkit::ferrerjulia2004::calc_ptf_ferrerjulia2004_rendzina_texture_organic_matter( 50.0, 25.0, 2.5); - assert_close_enough(result, 16.2905, 0.000000000001, 0.000000000001); + assert_close(result, 16.2905, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const auto result = ptfkit::ferrerjulia2004::calc_ptf_ferrerjulia2004_gleyic_solonchak_sand(50.0); - assert_close_enough(result, 4.883324693640427, 0.000000000001, 0.000000000001); + assert_close(result, 4.883324693640427, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const auto result = ptfkit::ferrerjulia2004:: calc_ptf_ferrerjulia2004_gleyic_solonchak_texture_organic_matter(50.0, 25.0, 2.5); - assert_close_enough(result, 10.53583, 0.000000000001, 0.000000000001); + assert_close(result, 10.53583, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const auto result = ptfkit::ferrerjulia2004::calc_ptf_ferrerjulia2004_general_sand(50.0); - assert_close_enough(result, 10.714718864969111, 0.000000000001, 0.000000000001); + assert_close(result, 10.714718864969111, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } { const auto result = ptfkit::ferrerjulia2004::calc_ptf_ferrerjulia2004_general_texture_organic_matter( 50.0, 25.0, 2.5); - assert_close_enough(result, 20.06325, 0.000000000001, 0.000000000001); + assert_close(result, 20.06325, 0.0001, 0.01, "saturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "reviewer_reference_vector"); } return 0; } diff --git a/targets/ptfkit-native/tests/cpp/hodnett2002.cpp b/targets/ptfkit-native/tests/cpp/hodnett2002.cpp index cf24e6f..06dfa8f 100644 --- a/targets/ptfkit-native/tests/cpp/hodnett2002.cpp +++ b/targets/ptfkit-native/tests/cpp/hodnett2002.cpp @@ -6,7 +6,7 @@ import ptfkit; import ptfkit.hodnett2002; #endif -#include "close_enough.h" +#include "support/close_enough.h" #include int main() { @@ -15,10 +15,14 @@ int main() { ptfkit::hodnett2002::calc_ptf_hodnett2002(39.2, 24.2, 36.7, 1.4, 1.2, 15.9, 5.85); static_assert(std::is_same_v, ptfkit::hodnett2002::Hodnett2002PTFResult>); - assert_close_enough(result.alpha, 0.245183615037873, 0.000000000001, 0.000000000001); - assert_close_enough(result.n, 1.36739326352383, 0.000000000001, 0.000000000001); - assert_close_enough(result.theta_s, 0.4993353, 0.000000000001, 0.000000000001); - assert_close_enough(result.theta_r, 0.21344216, 0.000000000001, 0.000000000001); + assert_close(result.alpha, 0.245183615037873, 0.00001, 0.0, "van_genuchten_alpha", + "inverse_kilopascal", "registry", "calibration_dataset_mean_properties"); + assert_close(result.n, 1.36739326352383, 0.001, 0.0, "van_genuchten_n", "dimensionless", + "registry", "calibration_dataset_mean_properties"); + assert_close(result.theta_s, 0.4993353, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "calibration_dataset_mean_properties"); + assert_close(result.theta_r, 0.21344216, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "calibration_dataset_mean_properties"); } return 0; } diff --git a/targets/ptfkit-native/tests/cpp/jabro1992.cpp b/targets/ptfkit-native/tests/cpp/jabro1992.cpp index 61753d3..3a1cba7 100644 --- a/targets/ptfkit-native/tests/cpp/jabro1992.cpp +++ b/targets/ptfkit-native/tests/cpp/jabro1992.cpp @@ -6,24 +6,31 @@ import ptfkit; import ptfkit.jabro1992; #endif -#include "close_enough.h" +#include "support/close_enough.h" int main() { { const auto result = ptfkit::jabro1992::calc_ptf_jabro1992(10.0, 5.0, 1.26); - assert_close_enough(result, 0.0003849640675896946, 0.000000000001, 0.00000001); + assert_close(result, 0.0003849640675896946, 0.0000000001, 0.01, + "saturated_hydraulic_conductivity", "meter_per_second", "registry", + "loamy_sand_min_bd"); } { const auto result = ptfkit::jabro1992::calc_ptf_jabro1992(38.72, 11.05, 1.42); - assert_close_enough(result, 0.000009804037952717678, 0.000000000001, 0.00000001); + assert_close(result, 0.000009804037952717678, 0.0000000001, 0.01, + "saturated_hydraulic_conductivity", "meter_per_second", "registry", "loam"); } { const auto result = ptfkit::jabro1992::calc_ptf_jabro1992(52.0, 30.0, 1.97); - assert_close_enough(result, 0.000000007292435947882127, 0.000000000001, 0.00000001); + assert_close(result, 0.000000007292435947882127, 0.0000000001, 0.01, + "saturated_hydraulic_conductivity", "meter_per_second", "registry", + "silty_clay_loam_max_silt"); } { const auto result = ptfkit::jabro1992::calc_ptf_jabro1992(0.2, 44.0, 1.61); - assert_close_enough(result, 0.00002032824027706267, 0.000000000001, 0.00000001); + assert_close(result, 0.00002032824027706267, 0.0000000001, 0.01, + "saturated_hydraulic_conductivity", "meter_per_second", "registry", + "sandy_clay_min_silt_max_clay"); } return 0; } diff --git a/targets/ptfkit-native/tests/cpp/li2007.cpp b/targets/ptfkit-native/tests/cpp/li2007.cpp index 917e0bc..957701b 100644 --- a/targets/ptfkit-native/tests/cpp/li2007.cpp +++ b/targets/ptfkit-native/tests/cpp/li2007.cpp @@ -6,7 +6,7 @@ import ptfkit; import ptfkit.li2007; #endif -#include "close_enough.h" +#include "support/close_enough.h" #include int main() { @@ -14,28 +14,42 @@ int main() { const auto result = ptfkit::li2007::calc_ptf_li2007(85.0, 10.0, 5.0, 1.2, 0.21); static_assert( std::is_same_v, ptfkit::li2007::Li2007PTFResult>); - assert_close_enough(result.theta_s, 0.5256803583157499, 0.000000000001, 0.00000001); - assert_close_enough(result.a_vg, 0.9491464758307142, 0.000000000001, 0.00000001); - assert_close_enough(result.n_vg, 1.1657804980997006, 0.000000000001, 0.00000001); - assert_close_enough(result.k_sat, 0.000006549110367333547, 0.000000000001, 0.00000001); + assert_close(result.theta_s, 0.5256803583157499, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "loamy_sand"); + assert_close(result.a_vg, 0.9491464758307142, 0.00001, 0.0, "van_genuchten_alpha", + "inverse_centimeter", "registry", "loamy_sand"); + assert_close(result.n_vg, 1.1657804980997006, 0.001, 0.0, "van_genuchten_n", + "dimensionless", "registry", "loamy_sand"); + assert_close(result.k_sat, 0.000006549110367333547, 0.0000000001, 0.01, + "saturated_hydraulic_conductivity", "meter_per_second", "registry", + "loamy_sand"); } { const auto result = ptfkit::li2007::calc_ptf_li2007(50.23, 38.72, 11.05, 1.42, 0.65); static_assert( std::is_same_v, ptfkit::li2007::Li2007PTFResult>); - assert_close_enough(result.theta_s, 0.49659526127697506, 0.000000000001, 0.00000001); - assert_close_enough(result.a_vg, 0.009519989841950734, 0.000000000001, 0.00000001); - assert_close_enough(result.n_vg, 1.1806286355149054, 0.000000000001, 0.00000001); - assert_close_enough(result.k_sat, 0.00000045117324656202257, 0.000000000001, 0.00000001); + assert_close(result.theta_s, 0.49659526127697506, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "loam"); + assert_close(result.a_vg, 0.009519989841950734, 0.00001, 0.0, "van_genuchten_alpha", + "inverse_centimeter", "registry", "loam"); + assert_close(result.n_vg, 1.1806286355149054, 0.001, 0.0, "van_genuchten_n", + "dimensionless", "registry", "loam"); + assert_close(result.k_sat, 0.00000045117324656202257, 0.0000000001, 0.01, + "saturated_hydraulic_conductivity", "meter_per_second", "registry", "loam"); } { const auto result = ptfkit::li2007::calc_ptf_li2007(12.88, 60.0, 27.12, 1.48, 1.02); static_assert( std::is_same_v, ptfkit::li2007::Li2007PTFResult>); - assert_close_enough(result.theta_s, 0.4053061510618609, 0.000000000001, 0.00000001); - assert_close_enough(result.a_vg, 0.0018530400762371828, 0.000000000001, 0.00000001); - assert_close_enough(result.n_vg, 1.2080428739797433, 0.000000000001, 0.00000001); - assert_close_enough(result.k_sat, 0.0000015151432632107234, 0.000000000001, 0.00000001); + assert_close(result.theta_s, 0.4053061510618609, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "silty_clay"); + assert_close(result.a_vg, 0.0018530400762371828, 0.00001, 0.0, "van_genuchten_alpha", + "inverse_centimeter", "registry", "silty_clay"); + assert_close(result.n_vg, 1.2080428739797433, 0.001, 0.0, "van_genuchten_n", + "dimensionless", "registry", "silty_clay"); + assert_close(result.k_sat, 0.0000015151432632107234, 0.0000000001, 0.01, + "saturated_hydraulic_conductivity", "meter_per_second", "registry", + "silty_clay"); } return 0; } diff --git a/targets/ptfkit-native/tests/cpp/mayr1999.cpp b/targets/ptfkit-native/tests/cpp/mayr1999.cpp index a6fd3db..a8dc624 100644 --- a/targets/ptfkit-native/tests/cpp/mayr1999.cpp +++ b/targets/ptfkit-native/tests/cpp/mayr1999.cpp @@ -6,7 +6,7 @@ import ptfkit; import ptfkit.mayr1999; #endif -#include "close_enough.h" +#include "support/close_enough.h" #include int main() { @@ -14,25 +14,34 @@ int main() { const auto result = ptfkit::mayr1999::calc_ptf_mayr1999(40.0, 40.0, 20.0, 1.3, 2.0); static_assert(std::is_same_v, ptfkit::mayr1999::Mayr1999PTFResult>); - assert_close_enough(result.a_hc, 9.19922782578422, 0.000000000001, 0.000000000001); - assert_close_enough(result.b_hc, 8.52432443785475, 0.000000000001, 0.000000000001); - assert_close_enough(result.theta_s, 0.48733161333, 0.000000000001, 0.000000000001); + assert_close(result.a_hc, 9.19922782578422, 0.01, 0.0, "matric_potential", + "centimeter_water_head", "registry", "representative_loam"); + assert_close(result.b_hc, 8.52432443785475, 0.001, 0.0, "hutson_cass_b", "dimensionless", + "registry", "representative_loam"); + assert_close(result.theta_s, 0.48733161333, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "representative_loam"); } { const auto result = ptfkit::mayr1999::calc_ptf_mayr1999(20.0, 20.0, 60.0, 1.2, 2.0); static_assert(std::is_same_v, ptfkit::mayr1999::Mayr1999PTFResult>); - assert_close_enough(result.a_hc, 14.5305137640911, 0.000000000001, 0.000000000001); - assert_close_enough(result.b_hc, 18.9375141190925, 0.000000000001, 0.000000000001); - assert_close_enough(result.theta_s, 0.52145684162, 0.000000000001, 0.000000000001); + assert_close(result.a_hc, 14.5305137640911, 0.01, 0.0, "matric_potential", + "centimeter_water_head", "registry", "representative_clay"); + assert_close(result.b_hc, 18.9375141190925, 0.001, 0.0, "hutson_cass_b", "dimensionless", + "registry", "representative_clay"); + assert_close(result.theta_s, 0.52145684162, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "representative_clay"); } { const auto result = ptfkit::mayr1999::calc_ptf_mayr1999(90.0, 5.0, 5.0, 1.5, 1.0); static_assert(std::is_same_v, ptfkit::mayr1999::Mayr1999PTFResult>); - assert_close_enough(result.a_hc, 2.40216699550362, 0.000000000001, 0.000000000001); - assert_close_enough(result.b_hc, 3.2690464531591, 0.000000000001, 0.000000000001); - assert_close_enough(result.theta_s, 0.4209756915, 0.000000000001, 0.000000000001); + assert_close(result.a_hc, 2.40216699550362, 0.01, 0.0, "matric_potential", + "centimeter_water_head", "registry", "representative_sand"); + assert_close(result.b_hc, 3.2690464531591, 0.001, 0.0, "hutson_cass_b", "dimensionless", + "registry", "representative_sand"); + assert_close(result.theta_s, 0.4209756915, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "representative_sand"); } return 0; } diff --git a/targets/ptfkit-native/tests/cpp/oosterveld1980.cpp b/targets/ptfkit-native/tests/cpp/oosterveld1980.cpp index b7856d7..41475ec 100644 --- a/targets/ptfkit-native/tests/cpp/oosterveld1980.cpp +++ b/targets/ptfkit-native/tests/cpp/oosterveld1980.cpp @@ -6,33 +6,38 @@ import ptfkit; import ptfkit.oosterveld1980; #endif -#include "close_enough.h" +#include "support/close_enough.h" int main() { { const auto result = ptfkit::oosterveld1980::calc_ptf_oosterveld1980_field_capacity_tension(6.6); - assert_close_enough(result, 11.8540994164288, 0.000000000001, 0.000000000001); + assert_close(result, 11.8540994164288, 0.01, 0.0, "matric_potential", "kilopascal", + "registry", "table_1_loamy_sand"); } { const auto result = ptfkit::oosterveld1980::calc_ptf_oosterveld1980_retention(6.6, 86.3, 105.0, 11.8); - assert_close_enough(result, 8.2782468100413, 0.000000000001, 0.000000000001); + assert_close(result, 8.2782468100413, 0.1, 0.0, "gravimetric_water_content", "mass_percent", + "registry", "table_1_loamy_sand_inputs"); } { const auto result = ptfkit::oosterveld1980::calc_ptf_oosterveld1980_field_capacity(6.6, 86.3, 105.0); - assert_close_enough(result, 8.14707947394088, 0.000000000001, 0.000000000001); + assert_close(result, 8.14707947394088, 0.1, 0.0, "gravimetric_water_content", + "mass_percent", "registry", "table_1_loamy_sand"); } { const auto result = ptfkit::oosterveld1980::calc_ptf_oosterveld1980_wilting_point(6.6, 86.3, 105.0); - assert_close_enough(result, 1.3942, 0.000000000001, 0.000000000001); + assert_close(result, 1.3942, 0.1, 0.0, "gravimetric_water_content", "mass_percent", + "registry", "table_1_loamy_sand"); } { const auto result = ptfkit::oosterveld1980::calc_ptf_oosterveld1980_available_water(6.6, 86.3, 105.0); - assert_close_enough(result, 6.75287947394088, 0.000000000001, 0.000000000001); + assert_close(result, 6.75287947394088, 0.1, 0.0, "gravimetric_water_content", + "mass_percent", "registry", "table_1_loamy_sand"); } return 0; } diff --git a/targets/ptfkit-native/tests/cpp/pidgeon1972.cpp b/targets/ptfkit-native/tests/cpp/pidgeon1972.cpp index 1078d81..c66ab99 100644 --- a/targets/ptfkit-native/tests/cpp/pidgeon1972.cpp +++ b/targets/ptfkit-native/tests/cpp/pidgeon1972.cpp @@ -6,83 +6,100 @@ import ptfkit; import ptfkit.pidgeon1972; #endif -#include "close_enough.h" +#include "support/close_enough.h" int main() { { const auto result = ptfkit::pidgeon1972::calc_ptf_pidgeon1972_fc(30.0, 20.0, 2.0); - assert_close_enough(result, 21.26, 0.000000000001, 0.000000000001); + assert_close(result, 21.26, 0.1, 0.0, "gravimetric_water_content", "mass_percent", + "registry", "regression_case"); } { const auto result = ptfkit::pidgeon1972::calc_ptf_pidgeon1972_fc_sand(50.0); - assert_close_enough(result, 23.66, 0.000000000001, 0.000000000001); + assert_close(result, 23.66, 0.1, 0.0, "gravimetric_water_content", "mass_percent", + "registry", "regression_case"); } { const auto result = ptfkit::pidgeon1972::calc_ptf_pidgeon1972_fc_sand_organic_matter(50.0, 2.0); - assert_close_enough(result, 23.27, 0.000000000001, 0.000000000001); + assert_close(result, 23.27, 0.1, 0.0, "gravimetric_water_content", "mass_percent", + "registry", "regression_case"); } { const auto result = ptfkit::pidgeon1972::calc_ptf_pidgeon1972_fc_vol_sand_organic_matter(50.0, 2.0); - assert_close_enough(result, 31.19, 0.000000000001, 0.000000000001); + assert_close(result, 31.19, 0.1, 0.0, "volumetric_water_content", "volume_percent", + "registry", "regression_case"); } { const auto result = ptfkit::pidgeon1972::calc_ptf_pidgeon1972_pwp(30.0, 20.0, 2.0); - assert_close_enough(result, 11.11, 0.000000000001, 0.000000000001); + assert_close(result, 11.11, 0.1, 0.0, "gravimetric_water_content", "mass_percent", + "registry", "regression_case"); } { const auto result = ptfkit::pidgeon1972::calc_ptf_pidgeon1972_pwp_sand(50.0); - assert_close_enough(result, 13.91, 0.000000000001, 0.000000000001); + assert_close(result, 13.91, 0.1, 0.0, "gravimetric_water_content", "mass_percent", + "registry", "regression_case"); } { const auto result = ptfkit::pidgeon1972::calc_ptf_pidgeon1972_pwp_sand_organic_matter(50.0, 2.0); - assert_close_enough(result, 15.28, 0.000000000001, 0.000000000001); + assert_close(result, 15.28, 0.1, 0.0, "gravimetric_water_content", "mass_percent", + "registry", "regression_case"); } { const auto result = ptfkit::pidgeon1972::calc_ptf_pidgeon1972_awc(20.0, 2.0); - assert_close_enough(result, 151.48, 0.000000000001, 0.000000000001); + assert_close(result, 151.48, 0.1, 0.0, "available_water_capacity", "millimeter_per_meter", + "registry", "regression_case"); } { const auto result = ptfkit::pidgeon1972::calc_ptf_pidgeon1972_awc_sand_organic_matter(50.0, 2.0); - assert_close_enough(result, 109.24, 0.000000000001, 0.000000000001); + assert_close(result, 109.24, 0.1, 0.0, "available_water_capacity", "millimeter_per_meter", + "registry", "regression_case"); } { const auto result = ptfkit::pidgeon1972::calc_ptf_pidgeon1972_awc_coarse_sand(20.0); - assert_close_enough(result, 115.1, 0.000000000001, 0.000000000001); + assert_close(result, 115.1, 0.1, 0.0, "available_water_capacity", "millimeter_per_meter", + "registry", "regression_case"); } { const auto result = ptfkit::pidgeon1972::calc_ptf_pidgeon1972_awc_fine_sand(20.0); - assert_close_enough(result, 119.9, 0.000000000001, 0.000000000001); + assert_close(result, 119.9, 0.1, 0.0, "available_water_capacity", "millimeter_per_meter", + "registry", "regression_case"); } { const auto result = ptfkit::pidgeon1972::calc_ptf_pidgeon1972_awc_very_fine_sand(10.0); - assert_close_enough(result, 112.7, 0.000000000001, 0.000000000001); + assert_close(result, 112.7, 0.1, 0.0, "available_water_capacity", "millimeter_per_meter", + "registry", "regression_case"); } { const auto result = ptfkit::pidgeon1972::calc_ptf_pidgeon1972_eawc(30.0, 20.0, 2.0); - assert_close_enough(result, 16.12, 0.000000000001, 0.000000000001); + assert_close(result, 16.12, 0.1, 0.0, "available_water_capacity", "millimeter_per_meter", + "registry", "regression_case"); } { const auto result = ptfkit::pidgeon1972::calc_ptf_pidgeon1972_eawc_sand(50.0); - assert_close_enough(result, 51.7, 0.000000000001, 0.000000000001); + assert_close(result, 51.7, 0.1, 0.0, "available_water_capacity", "millimeter_per_meter", + "registry", "regression_case"); } { const auto result = ptfkit::pidgeon1972::calc_ptf_pidgeon1972_eawc_sand_organic_matter(50.0, 2.0); - assert_close_enough(result, 56.26, 0.000000000001, 0.000000000001); + assert_close(result, 56.26, 0.1, 0.0, "available_water_capacity", "millimeter_per_meter", + "registry", "regression_case"); } { const auto result = ptfkit::pidgeon1972::calc_ptf_pidgeon1972_eawc_coarse_sand_organic_matter(20.0, 2.0); - assert_close_enough(result, 53.72, 0.000000000001, 0.000000000001); + assert_close(result, 53.72, 0.1, 0.0, "available_water_capacity", "millimeter_per_meter", + "registry", "regression_case"); } { const auto result = ptfkit::pidgeon1972::calc_ptf_pidgeon1972_eawc_fine_sand_organic_matter(20.0, 2.0); - assert_close_enough(result, 59.58, 0.000000000001, 0.000000000001); + assert_close(result, 59.58, 0.1, 0.0, "available_water_capacity", "millimeter_per_meter", + "registry", "regression_case"); } return 0; } diff --git a/targets/ptfkit-native/tests/cpp/puckett1985.cpp b/targets/ptfkit-native/tests/cpp/puckett1985.cpp index 95c453b..bf4173b 100644 --- a/targets/ptfkit-native/tests/cpp/puckett1985.cpp +++ b/targets/ptfkit-native/tests/cpp/puckett1985.cpp @@ -6,7 +6,7 @@ import ptfkit; import ptfkit.puckett1985; #endif -#include "close_enough.h" +#include "support/close_enough.h" #include int main() { @@ -14,17 +14,29 @@ int main() { const auto result = ptfkit::puckett1985::calc_ptf_puckett1985(70.9, 36.4, 11.8, 1.67, 0.38); static_assert(std::is_same_v, ptfkit::puckett1985::Puckett1985PTFResult>); - assert_close_enough(result.theta_0, 0.34288, 0.000000000001, 0.00000001); - assert_close_enough(result.theta_1, 0.33926, 0.000000000001, 0.00000001); - assert_close_enough(result.theta_5, 0.3938615, 0.000000000001, 0.00000001); - assert_close_enough(result.theta_10, 0.39330438, 0.000000000001, 0.00000001); - assert_close_enough(result.theta_30, 0.34432936, 0.000000000001, 0.00000001); - assert_close_enough(result.theta_60, 0.31153562, 0.000000000001, 0.00000001); - assert_close_enough(result.theta_100, 0.29292896, 0.000000000001, 0.00000001); - assert_close_enough(result.theta_500, 0.2513588, 0.000000000001, 0.00000001); - assert_close_enough(result.theta_1000, 0.25187788, 0.000000000001, 0.00000001); - assert_close_enough(result.theta_1500, 0.22746346, 0.000000000001, 0.00000001); - assert_close_enough(result.k_sat, 0.0000042399741, 0.000000000001, 0.00000001); + assert_close(result.theta_0, 0.34288, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "cahaba_ap"); + assert_close(result.theta_1, 0.33926, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "cahaba_ap"); + assert_close(result.theta_5, 0.3938615, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "cahaba_ap"); + assert_close(result.theta_10, 0.39330438, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "cahaba_ap"); + assert_close(result.theta_30, 0.34432936, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "cahaba_ap"); + assert_close(result.theta_60, 0.31153562, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "cahaba_ap"); + assert_close(result.theta_100, 0.29292896, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "cahaba_ap"); + assert_close(result.theta_500, 0.2513588, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "cahaba_ap"); + assert_close(result.theta_1000, 0.25187788, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "cahaba_ap"); + assert_close(result.theta_1500, 0.22746346, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "cahaba_ap"); + assert_close(result.k_sat, 0.0000042399741, 0.0000000001, 0.01, + "saturated_hydraulic_conductivity", "meter_per_second", "registry", + "cahaba_ap"); } return 0; } diff --git a/targets/ptfkit-native/tests/cpp/rawls1982.cpp b/targets/ptfkit-native/tests/cpp/rawls1982.cpp index c7e1e14..86b6222 100644 --- a/targets/ptfkit-native/tests/cpp/rawls1982.cpp +++ b/targets/ptfkit-native/tests/cpp/rawls1982.cpp @@ -6,35 +6,49 @@ import ptfkit; import ptfkit.rawls1982; #endif -#include "close_enough.h" +#include "support/close_enough.h" #include int main() { { const auto result = ptfkit::rawls1982::calc_ptf_rawls1982_theta_1500(5.12, 0.1); - assert_close_enough(result, 0.05318, 0.000000000001, 0.0000000001); + assert_close(result, 0.05318, 0.001, 0.0, "volumetric_water_content", "volume_fraction", + "registry", "loamy_sand"); } { const auto result = ptfkit::rawls1982::calc_ptf_rawls1982_theta_33(85.0, 0.1, 0.05318); - assert_close_enough(result, 0.1179896, 0.000000000001, 0.0000000001); + assert_close(result, 0.1179896, 0.001, 0.0, "volumetric_water_content", "volume_fraction", + "registry", "loamy_sand_with_estimated_theta_1500"); } { const auto result = ptfkit::rawls1982::calc_ptf_rawls1982_full_wrc(85.0, 0.66, 1.22, 0.091, 0.033); static_assert(std::is_same_v, ptfkit::rawls1982::Rawls1982PTFResult>); - assert_close_enough(result.theta_4, 0.247242, 0.000000000001, 0.0000000001); - assert_close_enough(result.theta_7, 0.968738, 0.000000000001, 0.0000000001); - assert_close_enough(result.theta_10, 0.145588, 0.000000000001, 0.0000000001); - assert_close_enough(result.theta_20, 0.10483, 0.000000000001, 0.0000000001); - assert_close_enough(result.theta_33, 0.091, 0.000000000001, 0.0000000001); - assert_close_enough(result.theta_60, 0.075428, 0.000000000001, 0.0000000001); - assert_close_enough(result.theta_100, 0.063192, 0.000000000001, 0.0000000001); - assert_close_enough(result.theta_200, 0.052946, 0.000000000001, 0.0000000001); - assert_close_enough(result.theta_400, 0.045826, 0.000000000001, 0.0000000001); - assert_close_enough(result.theta_700, 0.041824, 0.000000000001, 0.0000000001); - assert_close_enough(result.theta_1000, 0.038932, 0.000000000001, 0.0000000001); - assert_close_enough(result.theta_1500, 0.033, 0.000000000001, 0.0000000001); + assert_close(result.theta_4, 0.247242, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "loamy_sand"); + assert_close(result.theta_7, 0.968738, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "loamy_sand"); + assert_close(result.theta_10, 0.145588, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "loamy_sand"); + assert_close(result.theta_20, 0.10483, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "loamy_sand"); + assert_close(result.theta_33, 0.091, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "loamy_sand"); + assert_close(result.theta_60, 0.075428, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "loamy_sand"); + assert_close(result.theta_100, 0.063192, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "loamy_sand"); + assert_close(result.theta_200, 0.052946, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "loamy_sand"); + assert_close(result.theta_400, 0.045826, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "loamy_sand"); + assert_close(result.theta_700, 0.041824, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "loamy_sand"); + assert_close(result.theta_1000, 0.038932, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "loamy_sand"); + assert_close(result.theta_1500, 0.033, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "loamy_sand"); } return 0; } diff --git a/targets/ptfkit-native/tests/cpp/saxton2006.cpp b/targets/ptfkit-native/tests/cpp/saxton2006.cpp index 2e48738..1d02e3d 100644 --- a/targets/ptfkit-native/tests/cpp/saxton2006.cpp +++ b/targets/ptfkit-native/tests/cpp/saxton2006.cpp @@ -6,7 +6,7 @@ import ptfkit; import ptfkit.saxton2006; #endif -#include "close_enough.h" +#include "support/close_enough.h" #include int main() { @@ -14,75 +14,89 @@ int main() { const auto result = ptfkit::saxton2006::calc_ptf_saxton2006(0.88, 0.05, 2.5); static_assert(std::is_same_v, ptfkit::saxton2006::Saxton2006PTFResult>); - assert_close_enough(result.theta_1500, 0.05022058, 0.000000000001, 0.000000000001); - assert_close_enough(result.theta_33, 0.10282792364858702, 0.000000000001, 0.000000000001); - assert_close_enough(result.theta_s, 0.46172240764858724, 0.000000000001, 0.000000000001); - assert_close_enough(result.plant_available_water, 0.05260734364858702, 0.000000000001, - 0.000000000001); - assert_close_enough(result.air_entry_tension, 0.5986789729513293, 0.000000000001, - 0.000000000001); - assert_close_enough(result.retention_a, 0.00018076452552206025, 0.000000000001, - 0.000000000001); - assert_close_enough(result.retention_b, 5.325903100453899, 0.000000000001, 0.000000000001); - assert_close_enough(result.conductivity_lambda, 0.18776158355467926, 0.000000000001, - 0.000000000001); - assert_close_enough(result.saturated_conductivity, 108.1478278507403, 0.000000000001, - 0.000000000001); - assert_close_enough(result.normal_density, 1.4264356197312436, 0.000000000001, - 0.000000000001); + assert_close(result.theta_1500, 0.05022058, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "table_3_sand"); + assert_close(result.theta_33, 0.10282792364858702, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "table_3_sand"); + assert_close(result.theta_s, 0.46172240764858724, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "table_3_sand"); + assert_close(result.plant_available_water, 0.05260734364858702, 0.001, 0.0, + "volumetric_water_content", "volume_fraction", "registry", "table_3_sand"); + assert_close(result.air_entry_tension, 0.5986789729513293, 0.01, 0.0, "matric_potential", + "kilopascal", "registry", "table_3_sand"); + assert_close(result.retention_a, 0.00018076452552206025, 0.000000001, 0.01, + "retention_coefficient_a", "kilopascal", "registry", "table_3_sand"); + assert_close(result.retention_b, 5.325903100453899, 0.001, 0.0, "retention_coefficient_b", + "dimensionless", "registry", "table_3_sand"); + assert_close(result.conductivity_lambda, 0.18776158355467926, 0.001, 0.0, + "conductivity_shape_parameter", "dimensionless", "registry", "table_3_sand"); + assert_close(result.saturated_conductivity, 108.1478278507403, 0.0001, 0.01, + "saturated_hydraulic_conductivity", "millimeter_per_hour", "registry", + "table_3_sand"); + assert_close(result.normal_density, 1.4264356197312436, 0.001, 0.0, "dry_bulk_density", + "gram_per_cubic_centimeter", "registry", "table_3_sand"); } { const auto result = ptfkit::saxton2006::calc_ptf_saxton2006_density( 1.4264356197312436, 0.46172240764858724, 0.10282792364858702, 1.1); static_assert(std::is_same_v, ptfkit::saxton2006::Saxton2006DensityResult>); - assert_close_enough(result.adjusted_density, 1.569079181704368, 0.000000000001, - 0.000000000001); - assert_close_enough(result.adjusted_theta_s, 0.407894648413446, 0.000000000001, - 0.000000000001); - assert_close_enough(result.adjusted_theta_33, 0.09206237180155877, 0.000000000001, - 0.000000000001); - assert_close_enough(result.adjusted_theta_s_minus_33, 0.3158322766118873, 0.000000000001, - 0.000000000001); + assert_close(result.adjusted_density, 1.569079181704368, 0.001, 0.0, "dry_bulk_density", + "gram_per_cubic_centimeter", "registry", "table_3_sand_compacted"); + assert_close(result.adjusted_theta_s, 0.407894648413446, 0.001, 0.0, + "volumetric_water_content", "volume_fraction", "registry", + "table_3_sand_compacted"); + assert_close(result.adjusted_theta_33, 0.09206237180155877, 0.001, 0.0, + "volumetric_water_content", "volume_fraction", "registry", + "table_3_sand_compacted"); + assert_close(result.adjusted_theta_s_minus_33, 0.3158322766118873, 0.001, 0.0, + "volumetric_water_content", "volume_fraction", "registry", + "table_3_sand_compacted"); } { const auto result = ptfkit::saxton2006::calc_ptf_saxton2006_tension_dry( 0.07652425182429351, 0.05022058, 0.10282792364858702); - assert_close_enough(result, 159.18094591362183, 0.000000000001, 0.000000000001); + assert_close(result, 159.18094591362183, 0.01, 0.0, "matric_potential", "kilopascal", + "registry", "table_3_sand_midpoint"); } { const auto result = ptfkit::saxton2006::calc_ptf_saxton2006_tension_wet( 0.2, 0.10282792364858702, 0.46172240764858724, 0.5986789729513293); - assert_close_enough(result, 24.227216407352152, 0.000000000001, 0.000000000001); + assert_close(result, 24.227216407352152, 0.01, 0.0, "matric_potential", "kilopascal", + "registry", "table_3_sand_theta_0_2"); } { const auto result = ptfkit::saxton2006::calc_ptf_saxton2006_conductivity( 0.3, 0.46172240764858724, 108.1478278507403, 0.18776158355467926); - assert_close_enough(result, 0.3003203142764693, 0.000000000001, 0.000000000001); + assert_close(result, 0.3003203142764693, 0.0001, 0.01, "unsaturated_hydraulic_conductivity", + "millimeter_per_hour", "registry", "table_3_sand_theta_0_3"); } { const auto result = ptfkit::saxton2006::calc_ptf_saxton2006_gravel( 0.2, 1.4264356197312436, 0.05260734364858702, 108.1478278507403); static_assert(std::is_same_v, ptfkit::saxton2006::Saxton2006GravelResult>); - assert_close_enough(result.gravel_volume_fraction, 0.11860834455314037, 0.000000000001, - 0.000000000001); - assert_close_enough(result.bulk_density, 1.5715605653291098, 0.000000000001, - 0.000000000001); - assert_close_enough(result.bulk_plant_available_water, 0.04636767370708995, 0.000000000001, - 0.000000000001); - assert_close_enough(result.bulk_saturated_conductivity, 97.54880510583706, 0.000000000001, - 0.000000000001); + assert_close(result.gravel_volume_fraction, 0.11860834455314037, 0.001, 0.0, + "gravel_volume_fraction", "volume_fraction", "registry", + "table_3_sand_twenty_percent_gravel"); + assert_close(result.bulk_density, 1.5715605653291098, 0.001, 0.0, "dry_bulk_density", + "gram_per_cubic_centimeter", "registry", "table_3_sand_twenty_percent_gravel"); + assert_close(result.bulk_plant_available_water, 0.04636767370708995, 0.001, 0.0, + "volumetric_water_content", "volume_fraction", "registry", + "table_3_sand_twenty_percent_gravel"); + assert_close(result.bulk_saturated_conductivity, 97.54880510583706, 0.0001, 0.01, + "saturated_hydraulic_conductivity", "millimeter_per_hour", "registry", + "table_3_sand_twenty_percent_gravel"); } { const auto result = ptfkit::saxton2006::calc_ptf_saxton2006_salinity(4.0, 0.3, 0.46172240764858724); static_assert(std::is_same_v, ptfkit::saxton2006::Saxton2006SalinityResult>); - assert_close_enough(result.saturated_osmotic_potential, 144.0, 0.000000000001, - 0.000000000001); - assert_close_enough(result.osmotic_potential, 221.6267556713219, 0.000000000001, - 0.000000000001); + assert_close(result.saturated_osmotic_potential, 144.0, 0.01, 0.0, "osmotic_potential", + "kilopascal", "registry", "saline_partly_saturated_soil"); + assert_close(result.osmotic_potential, 221.6267556713219, 0.01, 0.0, "osmotic_potential", + "kilopascal", "registry", "saline_partly_saturated_soil"); } return 0; } diff --git a/targets/ptfkit-native/tests/cpp/tiwary2014.cpp b/targets/ptfkit-native/tests/cpp/tiwary2014.cpp index 54d24d3..2e8b147 100644 --- a/targets/ptfkit-native/tests/cpp/tiwary2014.cpp +++ b/targets/ptfkit-native/tests/cpp/tiwary2014.cpp @@ -6,23 +6,30 @@ import ptfkit; import ptfkit.tiwary2014; #endif -#include "close_enough.h" +#include "support/close_enough.h" #include int main() { { const auto result = ptfkit::tiwary2014::calc_ptf_tiwary2014_igp(37.3, 1.674, 4.6); - assert_close_enough(result, 0.0000005103578, 0.000000000001, 0.0000000001); + assert_close(result, 0.0000005103578, 0.0000000001, 0.01, + "saturated_hydraulic_conductivity", "meter_per_second", "registry", + "igp_saturated_conductivity_case"); } { const auto result = ptfkit::tiwary2014::calc_ptf_tiwary2014_bsr(54.9, 7.6, 61.6, 7.3, 21.4, 3.32); static_assert(std::is_same_v, ptfkit::tiwary2014::Tiwary2014PTFResult>); - assert_close_enough(result.w_33, 41.1729, 0.000000000001, 0.0000000001); - assert_close_enough(result.w_100, 36.8273, 0.000000000001, 0.0000000001); - assert_close_enough(result.w_1500, 21.6976, 0.000000000001, 0.0000000001); - assert_close_enough(result.k_sat, 0.000005373367, 0.000000000001, 0.0000000001); + assert_close(result.w_33, 41.1729, 0.1, 0.0, "gravimetric_water_content", "mass_percent", + "registry", "black_soil_compatibility_case"); + assert_close(result.w_100, 36.8273, 0.1, 0.0, "gravimetric_water_content", "mass_percent", + "registry", "black_soil_compatibility_case"); + assert_close(result.w_1500, 21.6976, 0.1, 0.0, "gravimetric_water_content", "mass_percent", + "registry", "black_soil_compatibility_case"); + assert_close(result.k_sat, 0.000005373367, 0.0000000001, 0.01, + "saturated_hydraulic_conductivity", "meter_per_second", "registry", + "black_soil_compatibility_case"); } return 0; } diff --git a/targets/ptfkit-native/tests/cpp/varallyai1982.cpp b/targets/ptfkit-native/tests/cpp/varallyai1982.cpp index 6485d7c..112627a 100644 --- a/targets/ptfkit-native/tests/cpp/varallyai1982.cpp +++ b/targets/ptfkit-native/tests/cpp/varallyai1982.cpp @@ -6,7 +6,7 @@ import ptfkit; import ptfkit.varallyai1982; #endif -#include "close_enough.h" +#include "support/close_enough.h" #include int main() { @@ -14,33 +14,45 @@ int main() { const auto result = ptfkit::varallyai1982::calc_ptf_varallyai1982_meadow(1.4, 0.3, 0.25); static_assert(std::is_same_v, ptfkit::varallyai1982::Varallyai1982Parameters>); - assert_close_enough(result.theta_0, 46.54495, 0.000000000001, 0.000000000001); - assert_close_enough(result.m, 0.10029, 0.000000000001, 0.000000000001); - assert_close_enough(result.pf_star, 3.62445, 0.000000000001, 0.000000000001); + assert_close(result.theta_0, 46.54495, 0.1, 0.0, "volumetric_water_content", + "volume_percent", "registry", "representative_review_case"); + assert_close(result.m, 0.10029, 0.001, 0.0, "varallyai_m", "dimensionless", "registry", + "representative_review_case"); + assert_close(result.pf_star, 3.62445, 0.001, 0.0, "varallyai_pf_star", "dimensionless", + "registry", "representative_review_case"); } { const auto result = ptfkit::varallyai1982::calc_ptf_varallyai1982_chernozem_a(1.4, 0.35); static_assert(std::is_same_v, ptfkit::varallyai1982::Varallyai1982Parameters>); - assert_close_enough(result.theta_0, 52.005, 0.000000000001, 0.000000000001); - assert_close_enough(result.m, 0.4174, 0.000000000001, 0.000000000001); - assert_close_enough(result.pf_star, 4.00469, 0.000000000001, 0.000000000001); + assert_close(result.theta_0, 52.005, 0.1, 0.0, "volumetric_water_content", "volume_percent", + "registry", "representative_review_case"); + assert_close(result.m, 0.4174, 0.001, 0.0, "varallyai_m", "dimensionless", "registry", + "representative_review_case"); + assert_close(result.pf_star, 4.00469, 0.001, 0.0, "varallyai_pf_star", "dimensionless", + "registry", "representative_review_case"); } { const auto result = ptfkit::varallyai1982::calc_ptf_varallyai1982_chernozem_b(1.4, 0.35); static_assert(std::is_same_v, ptfkit::varallyai1982::Varallyai1982Parameters>); - assert_close_enough(result.theta_0, 47.60035, 0.000000000001, 0.000000000001); - assert_close_enough(result.m, 0.407, 0.000000000001, 0.000000000001); - assert_close_enough(result.pf_star, 3.9299, 0.000000000001, 0.000000000001); + assert_close(result.theta_0, 47.60035, 0.1, 0.0, "volumetric_water_content", + "volume_percent", "registry", "representative_review_case"); + assert_close(result.m, 0.407, 0.001, 0.0, "varallyai_m", "dimensionless", "registry", + "representative_review_case"); + assert_close(result.pf_star, 3.9299, 0.001, 0.0, "varallyai_pf_star", "dimensionless", + "registry", "representative_review_case"); } { const auto result = ptfkit::varallyai1982::calc_ptf_varallyai1982_chernozem_c(1.4, 0.35); static_assert(std::is_same_v, ptfkit::varallyai1982::Varallyai1982Parameters>); - assert_close_enough(result.theta_0, 49.87, 0.000000000001, 0.000000000001); - assert_close_enough(result.m, 0.84011, 0.000000000001, 0.000000000001); - assert_close_enough(result.pf_star, 3.59772, 0.000000000001, 0.000000000001); + assert_close(result.theta_0, 49.87, 0.1, 0.0, "volumetric_water_content", "volume_percent", + "registry", "representative_review_case"); + assert_close(result.m, 0.84011, 0.001, 0.0, "varallyai_m", "dimensionless", "registry", + "representative_review_case"); + assert_close(result.pf_star, 3.59772, 0.001, 0.0, "varallyai_pf_star", "dimensionless", + "registry", "representative_review_case"); } return 0; } diff --git a/targets/ptfkit-native/tests/cpp/vereecken1989.cpp b/targets/ptfkit-native/tests/cpp/vereecken1989.cpp index 41b31c3..37e0104 100644 --- a/targets/ptfkit-native/tests/cpp/vereecken1989.cpp +++ b/targets/ptfkit-native/tests/cpp/vereecken1989.cpp @@ -6,7 +6,7 @@ import ptfkit; import ptfkit.vereecken1989; #endif -#include "close_enough.h" +#include "support/close_enough.h" #include int main() { @@ -15,20 +15,28 @@ int main() { ptfkit::vereecken1989::calc_ptf_vereecken1989(52.14, 10.93, 1.03, 1.466); static_assert(std::is_same_v, ptfkit::vereecken1989::Vereecken1989PTFResult>); - assert_close_enough(result.theta_r, 0.08406999999999999, 0.000000000001, 0.000000000001); - assert_close_enough(result.theta_s, 0.4060520000000001, 0.000000000001, 0.000000000001); - assert_close_enough(result.alpha, 0.003581613076579849, 0.000000000001, 0.000000000001); - assert_close_enough(result.n, 0.8602234826041976, 0.000000000001, 0.000000000001); + assert_close(result.theta_r, 0.08406999999999999, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "dataset_mean_properties"); + assert_close(result.theta_s, 0.4060520000000001, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "dataset_mean_properties"); + assert_close(result.alpha, 0.003581613076579849, 0.00001, 0.0, "van_genuchten_alpha", + "inverse_centimeter", "registry", "dataset_mean_properties"); + assert_close(result.n, 0.8602234826041976, 0.001, 0.0, "van_genuchten_n", "dimensionless", + "registry", "dataset_mean_properties"); } { const auto result = ptfkit::vereecken1989::calc_ptf_vereecken1989_detailed( 0.25, 0.88, 13.53, 21.15, 16.3, 24.83, 7.0, 5.15, 10.93, 0.07, 4.07, 1.03, 1.466); static_assert(std::is_same_v, ptfkit::vereecken1989::Vereecken1989DetailedPTFResult>); - assert_close_enough(result.theta_r, 0.08277820000000001, 0.000000000001, 0.000000000001); - assert_close_enough(result.theta_s, 0.4060520000000001, 0.000000000001, 0.000000000001); - assert_close_enough(result.alpha, 0.02522115884641546, 0.000000000001, 0.000000000001); - assert_close_enough(result.n, 0.517175605329397, 0.000000000001, 0.000000000001); + assert_close(result.theta_r, 0.08277820000000001, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "dataset_mean_properties"); + assert_close(result.theta_s, 0.4060520000000001, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "dataset_mean_properties"); + assert_close(result.alpha, 0.02522115884641546, 0.00001, 0.0, "van_genuchten_alpha", + "inverse_centimeter", "registry", "dataset_mean_properties"); + assert_close(result.n, 0.517175605329397, 0.001, 0.0, "van_genuchten_n", "dimensionless", + "registry", "dataset_mean_properties"); } return 0; } diff --git a/targets/ptfkit-native/tests/cpp/wang2012.cpp b/targets/ptfkit-native/tests/cpp/wang2012.cpp index 8f38765..b8f3586 100644 --- a/targets/ptfkit-native/tests/cpp/wang2012.cpp +++ b/targets/ptfkit-native/tests/cpp/wang2012.cpp @@ -6,7 +6,7 @@ import ptfkit; import ptfkit.wang2012; #endif -#include "close_enough.h" +#include "support/close_enough.h" #include int main() { @@ -15,9 +15,13 @@ int main() { ptfkit::wang2012::calc_ptf_wang2012(85.0, 10.0, 5.0, 1.22, 0.033, 1193.0); static_assert(std::is_same_v, ptfkit::wang2012::Wang2012PTFResult>); - assert_close_enough(result.theta_s, 0.61540575, 0.000000000001, 0.000001); - assert_close_enough(result.theta_fc, 0.38491949, 0.000000000001, 0.000001); - assert_close_enough(result.k_sat, 0.00003872974, 0.000000000001, 0.000001); + assert_close(result.theta_s, 0.61540575, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "derivation_minimum_soc_case"); + assert_close(result.theta_fc, 0.38491949, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "derivation_minimum_soc_case"); + assert_close(result.k_sat, 0.00003872974, 0.0000000001, 0.01, + "saturated_hydraulic_conductivity", "meter_per_second", "registry", + "derivation_minimum_soc_case"); } return 0; } diff --git a/targets/ptfkit-native/tests/cpp/weber2020.cpp b/targets/ptfkit-native/tests/cpp/weber2020.cpp index f63b7e8..5adec18 100644 --- a/targets/ptfkit-native/tests/cpp/weber2020.cpp +++ b/targets/ptfkit-native/tests/cpp/weber2020.cpp @@ -6,7 +6,7 @@ import ptfkit; import ptfkit.weber2020; #endif -#include "close_enough.h" +#include "support/close_enough.h" #include int main() { @@ -15,13 +15,22 @@ int main() { ptfkit::weber2020::calc_ptf_weber2020(0.05, 0.45, 0.02, 1.6, -0.5, 100.0); static_assert(std::is_same_v, ptfkit::weber2020::Weber2020PTFResult>); - assert_close_enough(result.theta_snc_bw, 0.06267, 0.000000000001, 0.000000000001); - assert_close_enough(result.theta_sc_bw, 0.38607, 0.000000000001, 0.000000000001); - assert_close_enough(result.alpha_bw, 0.0201472407335197, 0.000000000001, 0.000000000001); - assert_close_enough(result.n_bw, 1.71980542683289, 0.000000000001, 0.000000000001); - assert_close_enough(result.tau_bw, -0.887, 0.000000000001, 0.000000000001); - assert_close_enough(result.k_sc_bw, 172.186857498601, 0.000000000001, 0.000000000001); - assert_close_enough(result.k_snc_bw, 0.0190546071796325, 0.000000000001, 0.000000000001); + assert_close(result.theta_snc_bw, 0.06267, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "representative_vgm_parameters"); + assert_close(result.theta_sc_bw, 0.38607, 0.001, 0.0, "volumetric_water_content", + "volume_fraction", "registry", "representative_vgm_parameters"); + assert_close(result.alpha_bw, 0.0201472407335197, 0.00001, 0.0, "van_genuchten_alpha", + "inverse_centimeter", "registry", "representative_vgm_parameters"); + assert_close(result.n_bw, 1.71980542683289, 0.001, 0.0, "brunswick_n", "dimensionless", + "registry", "representative_vgm_parameters"); + assert_close(result.tau_bw, -0.887, 0.001, 0.0, "brunswick_tau", "dimensionless", + "registry", "representative_vgm_parameters"); + assert_close(result.k_sc_bw, 172.186857498601, 0.0001, 0.01, + "saturated_hydraulic_conductivity", "centimeter_per_day", "registry", + "representative_vgm_parameters"); + assert_close(result.k_snc_bw, 0.0190546071796325, 0.0001, 0.01, + "saturated_hydraulic_conductivity", "centimeter_per_day", "registry", + "representative_vgm_parameters"); } return 0; } diff --git a/targets/ptfkit-native/tests/support/close_enough.h b/targets/ptfkit-native/tests/support/close_enough.h new file mode 100644 index 0000000..9ce81e1 --- /dev/null +++ b/targets/ptfkit-native/tests/support/close_enough.h @@ -0,0 +1,71 @@ +/* @generated by ptfkit-codegen; DO NOT EDIT. */ + +#ifndef PTFKIT_TEST_CLOSE_ENOUGH_H +#define PTFKIT_TEST_CLOSE_ENOUGH_H + +#ifdef __cplusplus +#include +#include +#include +#include +#include + +inline double resolved_tolerance(double expected, double absolute, double relative) { + return std::max({absolute, relative * std::abs(expected), 0.00000000000001}); +} + +inline bool is_close(double actual, double expected, double absolute, double relative) { + return std::abs(actual - expected) <= resolved_tolerance(expected, absolute, relative); +} + +inline void _close_enough_impl(const char *file, int line, double actual, double expected, + double absolute, double relative, const char *quantity, + const char *unit, const char *source, const char *case_id) { + const double tolerance = resolved_tolerance(expected, absolute, relative); + const double difference = std::abs(actual - expected); + if (!is_close(actual, expected, absolute, relative)) { + std::println(stderr, + "assertion failed: {}:{}: case={}, actual={}, expected={}, difference={}, " + "tolerance={}, quantity={}, unit={}, source={}", + file, line, case_id, actual, expected, difference, tolerance, quantity, unit, + source); + std::exit(EXIT_FAILURE); + } +} + +#else +#include +#include +#include + +static inline double resolved_tolerance(double expected, double absolute, double relative) { + return fmax(fmax(absolute, relative * fabs(expected)), 0.00000000000001); +} + +static inline int is_close(double actual, double expected, double absolute, double relative) { + return fabs(actual - expected) <= resolved_tolerance(expected, absolute, relative); +} + +static inline void _close_enough_impl(const char *file, int line, double actual, double expected, + double absolute, double relative, const char *quantity, + const char *unit, const char *source, const char *case_id) { + const double tolerance = resolved_tolerance(expected, absolute, relative); + const double difference = fabs(actual - expected); + if (!is_close(actual, expected, absolute, relative)) { + fprintf(stderr, + "assertion failed: %s:%d: case=%s, actual=%.17g, expected=%.17g, difference=%.17g, " + "tolerance=%.17g, quantity=%s, unit=%s, source=%s\n", + file, line, case_id, actual, expected, difference, tolerance, quantity, unit, + source); + exit(EXIT_FAILURE); + } +} +#endif + +#define assert_close(actual, expected, absolute, relative, quantity, unit, source, case_id) \ + do { \ + _close_enough_impl(__FILE__, __LINE__, (actual), (expected), (absolute), (relative), \ + (quantity), (unit), (source), (case_id)); \ + } while (0) + +#endif diff --git a/targets/ptfkit-py/tests/_helpers.py b/targets/ptfkit-py/tests/_helpers.py index a45ebb9..744e0fd 100644 --- a/targets/ptfkit-py/tests/_helpers.py +++ b/targets/ptfkit-py/tests/_helpers.py @@ -1,3 +1,5 @@ +# @generated by ptfkit-codegen; DO NOT EDIT. + from __future__ import annotations from enum import Enum @@ -13,31 +15,28 @@ from typing import Any R = TypeVar('R', bound=NamedTuple) - - GoldenCase = tuple[Mapping[str, Any], dict[str, float], float, float] - VectorCasePart = tuple[ - dict[str, Any], - dict[str, float], - float, - float, - ] + Expected = dict[str, float] + VerificationCase = tuple[Mapping[str, Any], Expected] + VectorCasePart = tuple[dict[str, Any], Expected] VectorCaseScalar = tuple[*VectorCasePart, np.ndarray] VectorCaseTuple = tuple[*VectorCasePart, R] @overload -def prepare_vector_case(cases: Sequence[GoldenCase]) -> VectorCaseScalar: ... +def prepare_vector_case(cases: Sequence[VerificationCase]) -> VectorCaseScalar: ... @overload -def prepare_vector_case(cases: Sequence[GoldenCase], result_type: type[R]) -> VectorCaseTuple: ... +def prepare_vector_case( + cases: Sequence[VerificationCase], result_type: type[R] +) -> VectorCaseTuple: ... def prepare_vector_case( - cases: Sequence[GoldenCase], + cases: Sequence[VerificationCase], result_cls: type[R] | None = None, ) -> VectorCaseScalar | VectorCaseTuple: - inputs, expected, rtol, atol = cases[0] + inputs, expected = cases[0] vector_inputs = { name: ( EnumArray._from_members(type(value), [value]) # noqa: SLF001 @@ -47,11 +46,33 @@ def prepare_vector_case( for name, value in inputs.items() } out: np.ndarray | R - if result_cls is None: out = np.empty(1, dtype=float) else: field_count = len(result_cls._fields) out = result_cls(*(np.empty(1, dtype=float) for _ in range(field_count))) + return vector_inputs, expected, out + + +def assert_close( + actual: object, + expected: float, + *, + absolute: float, + relative: float, + quantity: str, + unit: str, + source: str, +) -> None: + actual_float = float(actual) # ty: ignore[invalid-argument-type] + tolerance = resolved_tolerance(expected, absolute, relative) + difference = abs(actual_float - expected) + assert difference <= tolerance, ( + f'actual={actual_float}, expected={expected}, difference={difference}, ' + f'tolerance={tolerance}, quantity={quantity!r}, unit={unit!r}, source={source}' + ) + - return vector_inputs, expected, rtol, atol, out +def resolved_tolerance(expected: float, absolute: float, relative: float) -> float: + scientific_tolerance = max(absolute, relative * abs(expected)) + return max(scientific_tolerance, 1e-14) diff --git a/targets/ptfkit-py/tests/test_ahuja1984.py b/targets/ptfkit-py/tests/test_ahuja1984.py index eaa55a9..6ae9a29 100644 --- a/targets/ptfkit-py/tests/test_ahuja1984.py +++ b/targets/ptfkit-py/tests/test_ahuja1984.py @@ -3,7 +3,7 @@ import pytest -from _helpers import prepare_vector_case +from _helpers import assert_close, prepare_vector_case from ptfkit.ahuja1984 import calc_ptf_ahuja1984 @@ -11,35 +11,59 @@ ( {'coefficient_b': 100.0, 'exponent_n': 4.0, 'theta_33': 0.25, 'total_porosity': 0.45}, {'k_sat': 0.16}, - 1e-12, - 1e-14, ), ( {'coefficient_b': 100.0, 'exponent_n': 5.0, 'theta_33': 0.2, 'total_porosity': 0.5}, {'k_sat': 0.243}, - 1e-12, - 1e-14, ), ] +CASES_CALC_PTF_AHUJA1984_IDS = [ + 'exponent_four', + 'exponent_five', +] -@pytest.mark.parametrize(('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_AHUJA1984) -def test_calc_ptf_ahuja1984_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float -): +@pytest.mark.parametrize( + ('inputs', 'expected'), CASES_CALC_PTF_AHUJA1984, ids=CASES_CALC_PTF_AHUJA1984_IDS +) +def test_calc_ptf_ahuja1984_verification(inputs: dict[str, float], expected: dict[str, float]): result = calc_ptf_ahuja1984(**inputs) - assert result == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result, + expected['k_sat'], + absolute=1e-5, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='centimeter_per_hour', + source='registry', + ) def test_calc_ptf_ahuja1984_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case(CASES_CALC_PTF_AHUJA1984) + inputs, expected, _out = prepare_vector_case(CASES_CALC_PTF_AHUJA1984) result = calc_ptf_ahuja1984(**inputs, out=None) - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=1e-5, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='centimeter_per_hour', + source='registry', + ) def test_calc_ptf_ahuja1984_out(): - inputs, expected, rtol, atol, out = prepare_vector_case(CASES_CALC_PTF_AHUJA1984) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_AHUJA1984) result = calc_ptf_ahuja1984(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=1e-5, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='centimeter_per_hour', + source='registry', + ) diff --git a/targets/ptfkit-py/tests/test_aimrun2009.py b/targets/ptfkit-py/tests/test_aimrun2009.py index 2cca14c..4401b87 100644 --- a/targets/ptfkit-py/tests/test_aimrun2009.py +++ b/targets/ptfkit-py/tests/test_aimrun2009.py @@ -3,7 +3,7 @@ import pytest -from _helpers import prepare_vector_case +from _helpers import assert_close, prepare_vector_case from ptfkit.aimrun2009 import calc_ptf_aimrun2009 @@ -11,47 +11,69 @@ ( {'bulk_density': 0.94, 'clay': 43.88, 'gmd': 0.01, 'organic_matter': 12.07}, {'k_sat': 7.358406556179513e-8}, - 1e-8, - 1e-12, ), ( {'bulk_density': 1.19, 'clay': 50.21, 'gmd': 0.007, 'organic_matter': 8.55}, {'k_sat': 3.07872446717209e-8}, - 1e-8, - 1e-12, ), ( {'bulk_density': 1.13, 'clay': 58.81, 'gmd': 0.005, 'organic_matter': 5.12}, {'k_sat': 2.3343051908963327e-8}, - 1e-8, - 1e-12, ), ( {'bulk_density': 1.08, 'clay': 47.5, 'gmd': 0.008, 'organic_matter': 1.43}, {'k_sat': 3.831168764444974e-8}, - 1e-8, - 1e-12, ), ] +CASES_CALC_PTF_AIMRUN2009_IDS = [ + 'mean_topsoil_layer', + 'mean_hardpan_layer', + 'mean_subsoil_layer', + 'min_organic_matter', +] -@pytest.mark.parametrize(('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_AIMRUN2009) -def test_calc_ptf_aimrun2009_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float -): +@pytest.mark.parametrize( + ('inputs', 'expected'), CASES_CALC_PTF_AIMRUN2009, ids=CASES_CALC_PTF_AIMRUN2009_IDS +) +def test_calc_ptf_aimrun2009_verification(inputs: dict[str, float], expected: dict[str, float]): result = calc_ptf_aimrun2009(**inputs) - assert result == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result, + expected['k_sat'], + absolute=1e-10, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='meter_per_second', + source='registry', + ) def test_calc_ptf_aimrun2009_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case(CASES_CALC_PTF_AIMRUN2009) + inputs, expected, _out = prepare_vector_case(CASES_CALC_PTF_AIMRUN2009) result = calc_ptf_aimrun2009(**inputs, out=None) - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=1e-10, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='meter_per_second', + source='registry', + ) def test_calc_ptf_aimrun2009_out(): - inputs, expected, rtol, atol, out = prepare_vector_case(CASES_CALC_PTF_AIMRUN2009) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_AIMRUN2009) result = calc_ptf_aimrun2009(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=1e-10, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='meter_per_second', + source='registry', + ) diff --git a/targets/ptfkit-py/tests/test_beniaich2023.py b/targets/ptfkit-py/tests/test_beniaich2023.py index 90472bf..5ed71dd 100644 --- a/targets/ptfkit-py/tests/test_beniaich2023.py +++ b/targets/ptfkit-py/tests/test_beniaich2023.py @@ -3,7 +3,7 @@ import pytest -from _helpers import prepare_vector_case +from _helpers import assert_close, prepare_vector_case from ptfkit.beniaich2023 import ( Beniaich2023PTFResult, calc_ptf_beniaich2023_mlr1, @@ -24,64 +24,123 @@ ( {'clay': 20.0}, { - 'water_field_capacity': 0.17577, 'water_saturation': 0.57427, + 'water_field_capacity': 0.17577, 'water_wilting_point': 0.09621, }, - 1e-12, - 1e-12, ), ] +CASES_CALC_PTF_BENIAICH2023_SLR1_IDS = [ + 'representative_case', +] -@pytest.mark.parametrize(('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_BENIAICH2023_SLR1) -def test_calc_ptf_beniaich2023_slr1_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +@pytest.mark.parametrize( + ('inputs', 'expected'), + CASES_CALC_PTF_BENIAICH2023_SLR1, + ids=CASES_CALC_PTF_BENIAICH2023_SLR1_IDS, +) +def test_calc_ptf_beniaich2023_slr1_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_beniaich2023_slr1(**inputs) - assert result.water_saturation == pytest.approx( - expected['water_saturation'], rel=rtol, abs=atol - ) - assert result.water_field_capacity == pytest.approx( - expected['water_field_capacity'], rel=rtol, abs=atol - ) - assert result.water_wilting_point == pytest.approx( - expected['water_wilting_point'], rel=rtol, abs=atol + assert_close( + result.water_saturation, + expected['water_saturation'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_field_capacity, + expected['water_field_capacity'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_wilting_point, + expected['water_wilting_point'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) def test_calc_ptf_beniaich2023_slr1_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_BENIAICH2023_SLR1, Beniaich2023PTFResult ) result = calc_ptf_beniaich2023_slr1(**inputs, out=None) - assert result.water_saturation[0] == pytest.approx( - expected['water_saturation'], rel=rtol, abs=atol - ) - assert result.water_field_capacity[0] == pytest.approx( - expected['water_field_capacity'], rel=rtol, abs=atol - ) - assert result.water_wilting_point[0] == pytest.approx( - expected['water_wilting_point'], rel=rtol, abs=atol + assert_close( + result.water_saturation[0], + expected['water_saturation'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_field_capacity[0], + expected['water_field_capacity'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_wilting_point[0], + expected['water_wilting_point'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) def test_calc_ptf_beniaich2023_slr1_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_BENIAICH2023_SLR1, Beniaich2023PTFResult ) result = calc_ptf_beniaich2023_slr1(**inputs, out=out) for actual, expected_out in zip(result, out, strict=True): assert actual is expected_out - assert result.water_saturation[0] == pytest.approx( - expected['water_saturation'], rel=rtol, abs=atol - ) - assert result.water_field_capacity[0] == pytest.approx( - expected['water_field_capacity'], rel=rtol, abs=atol - ) - assert result.water_wilting_point[0] == pytest.approx( - expected['water_wilting_point'], rel=rtol, abs=atol + assert_close( + result.water_saturation[0], + expected['water_saturation'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_field_capacity[0], + expected['water_field_capacity'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_wilting_point[0], + expected['water_wilting_point'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) @@ -89,64 +148,123 @@ def test_calc_ptf_beniaich2023_slr1_out(): ( {'silt': 30.0}, { - 'water_field_capacity': 0.24878, 'water_saturation': 0.68478, + 'water_field_capacity': 0.24878, 'water_wilting_point': 0.16131, }, - 1e-12, - 1e-12, ), ] +CASES_CALC_PTF_BENIAICH2023_SLR2_IDS = [ + 'representative_case', +] -@pytest.mark.parametrize(('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_BENIAICH2023_SLR2) -def test_calc_ptf_beniaich2023_slr2_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +@pytest.mark.parametrize( + ('inputs', 'expected'), + CASES_CALC_PTF_BENIAICH2023_SLR2, + ids=CASES_CALC_PTF_BENIAICH2023_SLR2_IDS, +) +def test_calc_ptf_beniaich2023_slr2_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_beniaich2023_slr2(**inputs) - assert result.water_saturation == pytest.approx( - expected['water_saturation'], rel=rtol, abs=atol - ) - assert result.water_field_capacity == pytest.approx( - expected['water_field_capacity'], rel=rtol, abs=atol - ) - assert result.water_wilting_point == pytest.approx( - expected['water_wilting_point'], rel=rtol, abs=atol + assert_close( + result.water_saturation, + expected['water_saturation'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_field_capacity, + expected['water_field_capacity'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_wilting_point, + expected['water_wilting_point'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) def test_calc_ptf_beniaich2023_slr2_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_BENIAICH2023_SLR2, Beniaich2023PTFResult ) result = calc_ptf_beniaich2023_slr2(**inputs, out=None) - assert result.water_saturation[0] == pytest.approx( - expected['water_saturation'], rel=rtol, abs=atol - ) - assert result.water_field_capacity[0] == pytest.approx( - expected['water_field_capacity'], rel=rtol, abs=atol - ) - assert result.water_wilting_point[0] == pytest.approx( - expected['water_wilting_point'], rel=rtol, abs=atol + assert_close( + result.water_saturation[0], + expected['water_saturation'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_field_capacity[0], + expected['water_field_capacity'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_wilting_point[0], + expected['water_wilting_point'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) def test_calc_ptf_beniaich2023_slr2_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_BENIAICH2023_SLR2, Beniaich2023PTFResult ) result = calc_ptf_beniaich2023_slr2(**inputs, out=out) for actual, expected_out in zip(result, out, strict=True): assert actual is expected_out - assert result.water_saturation[0] == pytest.approx( - expected['water_saturation'], rel=rtol, abs=atol - ) - assert result.water_field_capacity[0] == pytest.approx( - expected['water_field_capacity'], rel=rtol, abs=atol - ) - assert result.water_wilting_point[0] == pytest.approx( - expected['water_wilting_point'], rel=rtol, abs=atol + assert_close( + result.water_saturation[0], + expected['water_saturation'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_field_capacity[0], + expected['water_field_capacity'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_wilting_point[0], + expected['water_wilting_point'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) @@ -154,64 +272,123 @@ def test_calc_ptf_beniaich2023_slr2_out(): ( {'sand': 50.0}, { - 'water_field_capacity': 0.1848, 'water_saturation': 0.6007, + 'water_field_capacity': 0.1848, 'water_wilting_point': 0.11077, }, - 1e-12, - 1e-12, ), ] +CASES_CALC_PTF_BENIAICH2023_SLR3_IDS = [ + 'representative_case', +] -@pytest.mark.parametrize(('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_BENIAICH2023_SLR3) -def test_calc_ptf_beniaich2023_slr3_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +@pytest.mark.parametrize( + ('inputs', 'expected'), + CASES_CALC_PTF_BENIAICH2023_SLR3, + ids=CASES_CALC_PTF_BENIAICH2023_SLR3_IDS, +) +def test_calc_ptf_beniaich2023_slr3_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_beniaich2023_slr3(**inputs) - assert result.water_saturation == pytest.approx( - expected['water_saturation'], rel=rtol, abs=atol - ) - assert result.water_field_capacity == pytest.approx( - expected['water_field_capacity'], rel=rtol, abs=atol - ) - assert result.water_wilting_point == pytest.approx( - expected['water_wilting_point'], rel=rtol, abs=atol + assert_close( + result.water_saturation, + expected['water_saturation'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_field_capacity, + expected['water_field_capacity'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_wilting_point, + expected['water_wilting_point'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) def test_calc_ptf_beniaich2023_slr3_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_BENIAICH2023_SLR3, Beniaich2023PTFResult ) result = calc_ptf_beniaich2023_slr3(**inputs, out=None) - assert result.water_saturation[0] == pytest.approx( - expected['water_saturation'], rel=rtol, abs=atol - ) - assert result.water_field_capacity[0] == pytest.approx( - expected['water_field_capacity'], rel=rtol, abs=atol - ) - assert result.water_wilting_point[0] == pytest.approx( - expected['water_wilting_point'], rel=rtol, abs=atol + assert_close( + result.water_saturation[0], + expected['water_saturation'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_field_capacity[0], + expected['water_field_capacity'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_wilting_point[0], + expected['water_wilting_point'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) def test_calc_ptf_beniaich2023_slr3_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_BENIAICH2023_SLR3, Beniaich2023PTFResult ) result = calc_ptf_beniaich2023_slr3(**inputs, out=out) for actual, expected_out in zip(result, out, strict=True): assert actual is expected_out - assert result.water_saturation[0] == pytest.approx( - expected['water_saturation'], rel=rtol, abs=atol - ) - assert result.water_field_capacity[0] == pytest.approx( - expected['water_field_capacity'], rel=rtol, abs=atol - ) - assert result.water_wilting_point[0] == pytest.approx( - expected['water_wilting_point'], rel=rtol, abs=atol + assert_close( + result.water_saturation[0], + expected['water_saturation'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_field_capacity[0], + expected['water_field_capacity'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_wilting_point[0], + expected['water_wilting_point'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) @@ -219,64 +396,123 @@ def test_calc_ptf_beniaich2023_slr3_out(): ( {'clay': 20.0, 'silt': 30.0}, { - 'water_field_capacity': 0.30678, 'water_saturation': 0.74501, + 'water_field_capacity': 0.30678, 'water_wilting_point': 0.19915, }, - 1e-12, - 1e-12, ), ] +CASES_CALC_PTF_BENIAICH2023_SLR4_IDS = [ + 'representative_case', +] -@pytest.mark.parametrize(('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_BENIAICH2023_SLR4) -def test_calc_ptf_beniaich2023_slr4_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +@pytest.mark.parametrize( + ('inputs', 'expected'), + CASES_CALC_PTF_BENIAICH2023_SLR4, + ids=CASES_CALC_PTF_BENIAICH2023_SLR4_IDS, +) +def test_calc_ptf_beniaich2023_slr4_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_beniaich2023_slr4(**inputs) - assert result.water_saturation == pytest.approx( - expected['water_saturation'], rel=rtol, abs=atol - ) - assert result.water_field_capacity == pytest.approx( - expected['water_field_capacity'], rel=rtol, abs=atol - ) - assert result.water_wilting_point == pytest.approx( - expected['water_wilting_point'], rel=rtol, abs=atol + assert_close( + result.water_saturation, + expected['water_saturation'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_field_capacity, + expected['water_field_capacity'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_wilting_point, + expected['water_wilting_point'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) def test_calc_ptf_beniaich2023_slr4_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_BENIAICH2023_SLR4, Beniaich2023PTFResult ) result = calc_ptf_beniaich2023_slr4(**inputs, out=None) - assert result.water_saturation[0] == pytest.approx( - expected['water_saturation'], rel=rtol, abs=atol - ) - assert result.water_field_capacity[0] == pytest.approx( - expected['water_field_capacity'], rel=rtol, abs=atol - ) - assert result.water_wilting_point[0] == pytest.approx( - expected['water_wilting_point'], rel=rtol, abs=atol + assert_close( + result.water_saturation[0], + expected['water_saturation'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_field_capacity[0], + expected['water_field_capacity'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_wilting_point[0], + expected['water_wilting_point'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) def test_calc_ptf_beniaich2023_slr4_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_BENIAICH2023_SLR4, Beniaich2023PTFResult ) result = calc_ptf_beniaich2023_slr4(**inputs, out=out) for actual, expected_out in zip(result, out, strict=True): assert actual is expected_out - assert result.water_saturation[0] == pytest.approx( - expected['water_saturation'], rel=rtol, abs=atol - ) - assert result.water_field_capacity[0] == pytest.approx( - expected['water_field_capacity'], rel=rtol, abs=atol - ) - assert result.water_wilting_point[0] == pytest.approx( - expected['water_wilting_point'], rel=rtol, abs=atol + assert_close( + result.water_saturation[0], + expected['water_saturation'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_field_capacity[0], + expected['water_field_capacity'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_wilting_point[0], + expected['water_wilting_point'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) @@ -284,64 +520,123 @@ def test_calc_ptf_beniaich2023_slr4_out(): ( {'clay': 20.0, 'silt': 40.0}, { - 'water_field_capacity': 0.241875, 'water_saturation': 0.68578, + 'water_field_capacity': 0.241875, 'water_wilting_point': 0.16176, }, - 1e-12, - 1e-12, ), ] +CASES_CALC_PTF_BENIAICH2023_SLR5_IDS = [ + 'representative_case', +] -@pytest.mark.parametrize(('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_BENIAICH2023_SLR5) -def test_calc_ptf_beniaich2023_slr5_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +@pytest.mark.parametrize( + ('inputs', 'expected'), + CASES_CALC_PTF_BENIAICH2023_SLR5, + ids=CASES_CALC_PTF_BENIAICH2023_SLR5_IDS, +) +def test_calc_ptf_beniaich2023_slr5_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_beniaich2023_slr5(**inputs) - assert result.water_saturation == pytest.approx( - expected['water_saturation'], rel=rtol, abs=atol - ) - assert result.water_field_capacity == pytest.approx( - expected['water_field_capacity'], rel=rtol, abs=atol - ) - assert result.water_wilting_point == pytest.approx( - expected['water_wilting_point'], rel=rtol, abs=atol + assert_close( + result.water_saturation, + expected['water_saturation'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_field_capacity, + expected['water_field_capacity'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_wilting_point, + expected['water_wilting_point'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) def test_calc_ptf_beniaich2023_slr5_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_BENIAICH2023_SLR5, Beniaich2023PTFResult ) result = calc_ptf_beniaich2023_slr5(**inputs, out=None) - assert result.water_saturation[0] == pytest.approx( - expected['water_saturation'], rel=rtol, abs=atol - ) - assert result.water_field_capacity[0] == pytest.approx( - expected['water_field_capacity'], rel=rtol, abs=atol - ) - assert result.water_wilting_point[0] == pytest.approx( - expected['water_wilting_point'], rel=rtol, abs=atol + assert_close( + result.water_saturation[0], + expected['water_saturation'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_field_capacity[0], + expected['water_field_capacity'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_wilting_point[0], + expected['water_wilting_point'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) def test_calc_ptf_beniaich2023_slr5_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_BENIAICH2023_SLR5, Beniaich2023PTFResult ) result = calc_ptf_beniaich2023_slr5(**inputs, out=out) for actual, expected_out in zip(result, out, strict=True): assert actual is expected_out - assert result.water_saturation[0] == pytest.approx( - expected['water_saturation'], rel=rtol, abs=atol - ) - assert result.water_field_capacity[0] == pytest.approx( - expected['water_field_capacity'], rel=rtol, abs=atol - ) - assert result.water_wilting_point[0] == pytest.approx( - expected['water_wilting_point'], rel=rtol, abs=atol + assert_close( + result.water_saturation[0], + expected['water_saturation'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_field_capacity[0], + expected['water_field_capacity'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_wilting_point[0], + expected['water_wilting_point'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) @@ -349,64 +644,123 @@ def test_calc_ptf_beniaich2023_slr5_out(): ( {'soil_organic_matter': 2.0}, { - 'water_field_capacity': 0.24009, 'water_saturation': 0.66749, + 'water_field_capacity': 0.24009, 'water_wilting_point': 0.15562, }, - 1e-12, - 1e-12, ), ] +CASES_CALC_PTF_BENIAICH2023_SLR6_IDS = [ + 'representative_case', +] -@pytest.mark.parametrize(('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_BENIAICH2023_SLR6) -def test_calc_ptf_beniaich2023_slr6_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +@pytest.mark.parametrize( + ('inputs', 'expected'), + CASES_CALC_PTF_BENIAICH2023_SLR6, + ids=CASES_CALC_PTF_BENIAICH2023_SLR6_IDS, +) +def test_calc_ptf_beniaich2023_slr6_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_beniaich2023_slr6(**inputs) - assert result.water_saturation == pytest.approx( - expected['water_saturation'], rel=rtol, abs=atol - ) - assert result.water_field_capacity == pytest.approx( - expected['water_field_capacity'], rel=rtol, abs=atol - ) - assert result.water_wilting_point == pytest.approx( - expected['water_wilting_point'], rel=rtol, abs=atol + assert_close( + result.water_saturation, + expected['water_saturation'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_field_capacity, + expected['water_field_capacity'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_wilting_point, + expected['water_wilting_point'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) def test_calc_ptf_beniaich2023_slr6_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_BENIAICH2023_SLR6, Beniaich2023PTFResult ) result = calc_ptf_beniaich2023_slr6(**inputs, out=None) - assert result.water_saturation[0] == pytest.approx( - expected['water_saturation'], rel=rtol, abs=atol - ) - assert result.water_field_capacity[0] == pytest.approx( - expected['water_field_capacity'], rel=rtol, abs=atol - ) - assert result.water_wilting_point[0] == pytest.approx( - expected['water_wilting_point'], rel=rtol, abs=atol + assert_close( + result.water_saturation[0], + expected['water_saturation'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_field_capacity[0], + expected['water_field_capacity'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_wilting_point[0], + expected['water_wilting_point'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) def test_calc_ptf_beniaich2023_slr6_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_BENIAICH2023_SLR6, Beniaich2023PTFResult ) result = calc_ptf_beniaich2023_slr6(**inputs, out=out) for actual, expected_out in zip(result, out, strict=True): assert actual is expected_out - assert result.water_saturation[0] == pytest.approx( - expected['water_saturation'], rel=rtol, abs=atol - ) - assert result.water_field_capacity[0] == pytest.approx( - expected['water_field_capacity'], rel=rtol, abs=atol - ) - assert result.water_wilting_point[0] == pytest.approx( - expected['water_wilting_point'], rel=rtol, abs=atol + assert_close( + result.water_saturation[0], + expected['water_saturation'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_field_capacity[0], + expected['water_field_capacity'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_wilting_point[0], + expected['water_wilting_point'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) @@ -414,64 +768,123 @@ def test_calc_ptf_beniaich2023_slr6_out(): ( {'sand': 50.0, 'silt': 30.0, 'soil_organic_matter': 2.0}, { - 'water_field_capacity': 0.17238, 'water_saturation': 0.56266, + 'water_field_capacity': 0.17238, 'water_wilting_point': 0.09366, }, - 1e-12, - 1e-12, ), ] +CASES_CALC_PTF_BENIAICH2023_MLR1_IDS = [ + 'representative_case', +] -@pytest.mark.parametrize(('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_BENIAICH2023_MLR1) -def test_calc_ptf_beniaich2023_mlr1_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +@pytest.mark.parametrize( + ('inputs', 'expected'), + CASES_CALC_PTF_BENIAICH2023_MLR1, + ids=CASES_CALC_PTF_BENIAICH2023_MLR1_IDS, +) +def test_calc_ptf_beniaich2023_mlr1_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_beniaich2023_mlr1(**inputs) - assert result.water_saturation == pytest.approx( - expected['water_saturation'], rel=rtol, abs=atol - ) - assert result.water_field_capacity == pytest.approx( - expected['water_field_capacity'], rel=rtol, abs=atol - ) - assert result.water_wilting_point == pytest.approx( - expected['water_wilting_point'], rel=rtol, abs=atol + assert_close( + result.water_saturation, + expected['water_saturation'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_field_capacity, + expected['water_field_capacity'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_wilting_point, + expected['water_wilting_point'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) def test_calc_ptf_beniaich2023_mlr1_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_BENIAICH2023_MLR1, Beniaich2023PTFResult ) result = calc_ptf_beniaich2023_mlr1(**inputs, out=None) - assert result.water_saturation[0] == pytest.approx( - expected['water_saturation'], rel=rtol, abs=atol - ) - assert result.water_field_capacity[0] == pytest.approx( - expected['water_field_capacity'], rel=rtol, abs=atol - ) - assert result.water_wilting_point[0] == pytest.approx( - expected['water_wilting_point'], rel=rtol, abs=atol + assert_close( + result.water_saturation[0], + expected['water_saturation'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_field_capacity[0], + expected['water_field_capacity'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_wilting_point[0], + expected['water_wilting_point'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) def test_calc_ptf_beniaich2023_mlr1_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_BENIAICH2023_MLR1, Beniaich2023PTFResult ) result = calc_ptf_beniaich2023_mlr1(**inputs, out=out) for actual, expected_out in zip(result, out, strict=True): assert actual is expected_out - assert result.water_saturation[0] == pytest.approx( - expected['water_saturation'], rel=rtol, abs=atol - ) - assert result.water_field_capacity[0] == pytest.approx( - expected['water_field_capacity'], rel=rtol, abs=atol - ) - assert result.water_wilting_point[0] == pytest.approx( - expected['water_wilting_point'], rel=rtol, abs=atol + assert_close( + result.water_saturation[0], + expected['water_saturation'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_field_capacity[0], + expected['water_field_capacity'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_wilting_point[0], + expected['water_wilting_point'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) @@ -479,64 +892,123 @@ def test_calc_ptf_beniaich2023_mlr1_out(): ( {'sand': 50.0, 'soil_organic_matter': 2.0}, { - 'water_field_capacity': 0.18025, 'water_saturation': 0.58954, + 'water_field_capacity': 0.18025, 'water_wilting_point': 0.10825, }, - 1e-12, - 1e-12, ), ] +CASES_CALC_PTF_BENIAICH2023_MLR2_IDS = [ + 'representative_case', +] -@pytest.mark.parametrize(('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_BENIAICH2023_MLR2) -def test_calc_ptf_beniaich2023_mlr2_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +@pytest.mark.parametrize( + ('inputs', 'expected'), + CASES_CALC_PTF_BENIAICH2023_MLR2, + ids=CASES_CALC_PTF_BENIAICH2023_MLR2_IDS, +) +def test_calc_ptf_beniaich2023_mlr2_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_beniaich2023_mlr2(**inputs) - assert result.water_saturation == pytest.approx( - expected['water_saturation'], rel=rtol, abs=atol - ) - assert result.water_field_capacity == pytest.approx( - expected['water_field_capacity'], rel=rtol, abs=atol - ) - assert result.water_wilting_point == pytest.approx( - expected['water_wilting_point'], rel=rtol, abs=atol + assert_close( + result.water_saturation, + expected['water_saturation'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_field_capacity, + expected['water_field_capacity'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_wilting_point, + expected['water_wilting_point'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) def test_calc_ptf_beniaich2023_mlr2_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_BENIAICH2023_MLR2, Beniaich2023PTFResult ) result = calc_ptf_beniaich2023_mlr2(**inputs, out=None) - assert result.water_saturation[0] == pytest.approx( - expected['water_saturation'], rel=rtol, abs=atol - ) - assert result.water_field_capacity[0] == pytest.approx( - expected['water_field_capacity'], rel=rtol, abs=atol - ) - assert result.water_wilting_point[0] == pytest.approx( - expected['water_wilting_point'], rel=rtol, abs=atol + assert_close( + result.water_saturation[0], + expected['water_saturation'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_field_capacity[0], + expected['water_field_capacity'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_wilting_point[0], + expected['water_wilting_point'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) def test_calc_ptf_beniaich2023_mlr2_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_BENIAICH2023_MLR2, Beniaich2023PTFResult ) result = calc_ptf_beniaich2023_mlr2(**inputs, out=out) for actual, expected_out in zip(result, out, strict=True): assert actual is expected_out - assert result.water_saturation[0] == pytest.approx( - expected['water_saturation'], rel=rtol, abs=atol - ) - assert result.water_field_capacity[0] == pytest.approx( - expected['water_field_capacity'], rel=rtol, abs=atol - ) - assert result.water_wilting_point[0] == pytest.approx( - expected['water_wilting_point'], rel=rtol, abs=atol + assert_close( + result.water_saturation[0], + expected['water_saturation'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_field_capacity[0], + expected['water_field_capacity'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_wilting_point[0], + expected['water_wilting_point'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) @@ -544,64 +1016,123 @@ def test_calc_ptf_beniaich2023_mlr2_out(): ( {'silt': 30.0, 'soil_organic_matter': 2.0}, { - 'water_field_capacity': 0.24275, 'water_saturation': 0.67031, + 'water_field_capacity': 0.24275, 'water_wilting_point': 0.15755, }, - 1e-12, - 1e-12, ), ] +CASES_CALC_PTF_BENIAICH2023_MLR3_IDS = [ + 'representative_case', +] -@pytest.mark.parametrize(('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_BENIAICH2023_MLR3) -def test_calc_ptf_beniaich2023_mlr3_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +@pytest.mark.parametrize( + ('inputs', 'expected'), + CASES_CALC_PTF_BENIAICH2023_MLR3, + ids=CASES_CALC_PTF_BENIAICH2023_MLR3_IDS, +) +def test_calc_ptf_beniaich2023_mlr3_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_beniaich2023_mlr3(**inputs) - assert result.water_saturation == pytest.approx( - expected['water_saturation'], rel=rtol, abs=atol - ) - assert result.water_field_capacity == pytest.approx( - expected['water_field_capacity'], rel=rtol, abs=atol - ) - assert result.water_wilting_point == pytest.approx( - expected['water_wilting_point'], rel=rtol, abs=atol + assert_close( + result.water_saturation, + expected['water_saturation'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_field_capacity, + expected['water_field_capacity'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_wilting_point, + expected['water_wilting_point'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) def test_calc_ptf_beniaich2023_mlr3_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_BENIAICH2023_MLR3, Beniaich2023PTFResult ) result = calc_ptf_beniaich2023_mlr3(**inputs, out=None) - assert result.water_saturation[0] == pytest.approx( - expected['water_saturation'], rel=rtol, abs=atol - ) - assert result.water_field_capacity[0] == pytest.approx( - expected['water_field_capacity'], rel=rtol, abs=atol - ) - assert result.water_wilting_point[0] == pytest.approx( - expected['water_wilting_point'], rel=rtol, abs=atol + assert_close( + result.water_saturation[0], + expected['water_saturation'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_field_capacity[0], + expected['water_field_capacity'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_wilting_point[0], + expected['water_wilting_point'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) def test_calc_ptf_beniaich2023_mlr3_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_BENIAICH2023_MLR3, Beniaich2023PTFResult ) result = calc_ptf_beniaich2023_mlr3(**inputs, out=out) for actual, expected_out in zip(result, out, strict=True): assert actual is expected_out - assert result.water_saturation[0] == pytest.approx( - expected['water_saturation'], rel=rtol, abs=atol - ) - assert result.water_field_capacity[0] == pytest.approx( - expected['water_field_capacity'], rel=rtol, abs=atol - ) - assert result.water_wilting_point[0] == pytest.approx( - expected['water_wilting_point'], rel=rtol, abs=atol + assert_close( + result.water_saturation[0], + expected['water_saturation'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_field_capacity[0], + expected['water_field_capacity'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_wilting_point[0], + expected['water_wilting_point'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) @@ -609,64 +1140,123 @@ def test_calc_ptf_beniaich2023_mlr3_out(): ( {'clay': 20.0, 'soil_organic_matter': 2.0}, { - 'water_field_capacity': 0.16859, 'water_saturation': 0.5589, + 'water_field_capacity': 0.16859, 'water_wilting_point': 0.09157, }, - 1e-12, - 1e-12, ), ] +CASES_CALC_PTF_BENIAICH2023_MLR4_IDS = [ + 'representative_case', +] -@pytest.mark.parametrize(('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_BENIAICH2023_MLR4) -def test_calc_ptf_beniaich2023_mlr4_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +@pytest.mark.parametrize( + ('inputs', 'expected'), + CASES_CALC_PTF_BENIAICH2023_MLR4, + ids=CASES_CALC_PTF_BENIAICH2023_MLR4_IDS, +) +def test_calc_ptf_beniaich2023_mlr4_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_beniaich2023_mlr4(**inputs) - assert result.water_saturation == pytest.approx( - expected['water_saturation'], rel=rtol, abs=atol - ) - assert result.water_field_capacity == pytest.approx( - expected['water_field_capacity'], rel=rtol, abs=atol - ) - assert result.water_wilting_point == pytest.approx( - expected['water_wilting_point'], rel=rtol, abs=atol + assert_close( + result.water_saturation, + expected['water_saturation'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_field_capacity, + expected['water_field_capacity'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_wilting_point, + expected['water_wilting_point'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) def test_calc_ptf_beniaich2023_mlr4_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_BENIAICH2023_MLR4, Beniaich2023PTFResult ) result = calc_ptf_beniaich2023_mlr4(**inputs, out=None) - assert result.water_saturation[0] == pytest.approx( - expected['water_saturation'], rel=rtol, abs=atol - ) - assert result.water_field_capacity[0] == pytest.approx( - expected['water_field_capacity'], rel=rtol, abs=atol - ) - assert result.water_wilting_point[0] == pytest.approx( - expected['water_wilting_point'], rel=rtol, abs=atol + assert_close( + result.water_saturation[0], + expected['water_saturation'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_field_capacity[0], + expected['water_field_capacity'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_wilting_point[0], + expected['water_wilting_point'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) def test_calc_ptf_beniaich2023_mlr4_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_BENIAICH2023_MLR4, Beniaich2023PTFResult ) result = calc_ptf_beniaich2023_mlr4(**inputs, out=out) for actual, expected_out in zip(result, out, strict=True): assert actual is expected_out - assert result.water_saturation[0] == pytest.approx( - expected['water_saturation'], rel=rtol, abs=atol - ) - assert result.water_field_capacity[0] == pytest.approx( - expected['water_field_capacity'], rel=rtol, abs=atol - ) - assert result.water_wilting_point[0] == pytest.approx( - expected['water_wilting_point'], rel=rtol, abs=atol + assert_close( + result.water_saturation[0], + expected['water_saturation'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_field_capacity[0], + expected['water_field_capacity'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_wilting_point[0], + expected['water_wilting_point'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) @@ -674,62 +1264,121 @@ def test_calc_ptf_beniaich2023_mlr4_out(): ( {'clay': 20.0, 'silt': 30.0, 'soil_organic_matter': 2.0}, { - 'water_field_capacity': 0.172, 'water_saturation': 0.56229, + 'water_field_capacity': 0.172, 'water_wilting_point': 0.09379, }, - 1e-12, - 1e-12, ), ] +CASES_CALC_PTF_BENIAICH2023_MLR5_IDS = [ + 'representative_case', +] -@pytest.mark.parametrize(('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_BENIAICH2023_MLR5) -def test_calc_ptf_beniaich2023_mlr5_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +@pytest.mark.parametrize( + ('inputs', 'expected'), + CASES_CALC_PTF_BENIAICH2023_MLR5, + ids=CASES_CALC_PTF_BENIAICH2023_MLR5_IDS, +) +def test_calc_ptf_beniaich2023_mlr5_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_beniaich2023_mlr5(**inputs) - assert result.water_saturation == pytest.approx( - expected['water_saturation'], rel=rtol, abs=atol - ) - assert result.water_field_capacity == pytest.approx( - expected['water_field_capacity'], rel=rtol, abs=atol - ) - assert result.water_wilting_point == pytest.approx( - expected['water_wilting_point'], rel=rtol, abs=atol + assert_close( + result.water_saturation, + expected['water_saturation'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_field_capacity, + expected['water_field_capacity'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_wilting_point, + expected['water_wilting_point'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) def test_calc_ptf_beniaich2023_mlr5_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_BENIAICH2023_MLR5, Beniaich2023PTFResult ) result = calc_ptf_beniaich2023_mlr5(**inputs, out=None) - assert result.water_saturation[0] == pytest.approx( - expected['water_saturation'], rel=rtol, abs=atol - ) - assert result.water_field_capacity[0] == pytest.approx( - expected['water_field_capacity'], rel=rtol, abs=atol - ) - assert result.water_wilting_point[0] == pytest.approx( - expected['water_wilting_point'], rel=rtol, abs=atol + assert_close( + result.water_saturation[0], + expected['water_saturation'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_field_capacity[0], + expected['water_field_capacity'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_wilting_point[0], + expected['water_wilting_point'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) def test_calc_ptf_beniaich2023_mlr5_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_BENIAICH2023_MLR5, Beniaich2023PTFResult ) result = calc_ptf_beniaich2023_mlr5(**inputs, out=out) for actual, expected_out in zip(result, out, strict=True): assert actual is expected_out - assert result.water_saturation[0] == pytest.approx( - expected['water_saturation'], rel=rtol, abs=atol - ) - assert result.water_field_capacity[0] == pytest.approx( - expected['water_field_capacity'], rel=rtol, abs=atol - ) - assert result.water_wilting_point[0] == pytest.approx( - expected['water_wilting_point'], rel=rtol, abs=atol + assert_close( + result.water_saturation[0], + expected['water_saturation'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_field_capacity[0], + expected['water_field_capacity'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_wilting_point[0], + expected['water_wilting_point'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) diff --git a/targets/ptfkit-py/tests/test_chakraborty2011.py b/targets/ptfkit-py/tests/test_chakraborty2011.py index 65af064..0c05c65 100644 --- a/targets/ptfkit-py/tests/test_chakraborty2011.py +++ b/targets/ptfkit-py/tests/test_chakraborty2011.py @@ -3,7 +3,7 @@ import pytest -from _helpers import prepare_vector_case +from _helpers import assert_close, prepare_vector_case from ptfkit.chakraborty2011 import ( Chakraborty2011PTFResult, calc_ptf_chakraborty2011_eq1, @@ -19,74 +19,151 @@ ( {'clay': 20.0, 'silt': 30.0}, { - 'water_content_100': 0.19758, - 'water_content_1500': 0.11702, 'water_content_33': 0.2488, + 'water_content_100': 0.19758, 'water_content_500': 0.13528, + 'water_content_1500': 0.11702, }, - 1e-12, - 1e-12, ), ] +CASES_CALC_PTF_CHAKRABORTY2011_EQ1_IDS = [ + 'representative_case', +] -@pytest.mark.parametrize(('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_CHAKRABORTY2011_EQ1) -def test_calc_ptf_chakraborty2011_eq1_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +@pytest.mark.parametrize( + ('inputs', 'expected'), + CASES_CALC_PTF_CHAKRABORTY2011_EQ1, + ids=CASES_CALC_PTF_CHAKRABORTY2011_EQ1_IDS, +) +def test_calc_ptf_chakraborty2011_eq1_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_chakraborty2011_eq1(**inputs) - assert result.water_content_33 == pytest.approx( - expected['water_content_33'], rel=rtol, abs=atol - ) - assert result.water_content_100 == pytest.approx( - expected['water_content_100'], rel=rtol, abs=atol - ) - assert result.water_content_500 == pytest.approx( - expected['water_content_500'], rel=rtol, abs=atol - ) - assert result.water_content_1500 == pytest.approx( - expected['water_content_1500'], rel=rtol, abs=atol + assert_close( + result.water_content_33, + expected['water_content_33'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_100, + expected['water_content_100'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_500, + expected['water_content_500'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_1500, + expected['water_content_1500'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) def test_calc_ptf_chakraborty2011_eq1_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_CHAKRABORTY2011_EQ1, Chakraborty2011PTFResult ) result = calc_ptf_chakraborty2011_eq1(**inputs, out=None) - assert result.water_content_33[0] == pytest.approx( - expected['water_content_33'], rel=rtol, abs=atol - ) - assert result.water_content_100[0] == pytest.approx( - expected['water_content_100'], rel=rtol, abs=atol - ) - assert result.water_content_500[0] == pytest.approx( - expected['water_content_500'], rel=rtol, abs=atol - ) - assert result.water_content_1500[0] == pytest.approx( - expected['water_content_1500'], rel=rtol, abs=atol + assert_close( + result.water_content_33[0], + expected['water_content_33'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_100[0], + expected['water_content_100'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_500[0], + expected['water_content_500'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_1500[0], + expected['water_content_1500'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) def test_calc_ptf_chakraborty2011_eq1_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_CHAKRABORTY2011_EQ1, Chakraborty2011PTFResult ) result = calc_ptf_chakraborty2011_eq1(**inputs, out=out) for actual, expected_out in zip(result, out, strict=True): assert actual is expected_out - assert result.water_content_33[0] == pytest.approx( - expected['water_content_33'], rel=rtol, abs=atol - ) - assert result.water_content_100[0] == pytest.approx( - expected['water_content_100'], rel=rtol, abs=atol - ) - assert result.water_content_500[0] == pytest.approx( - expected['water_content_500'], rel=rtol, abs=atol - ) - assert result.water_content_1500[0] == pytest.approx( - expected['water_content_1500'], rel=rtol, abs=atol + assert_close( + result.water_content_33[0], + expected['water_content_33'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_100[0], + expected['water_content_100'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_500[0], + expected['water_content_500'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_1500[0], + expected['water_content_1500'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) @@ -94,74 +171,151 @@ def test_calc_ptf_chakraborty2011_eq1_out(): ( {'bulk_density': 1.5, 'sand': 50.0}, { - 'water_content_100': 0.175765, - 'water_content_1500': 0.118315, 'water_content_33': 0.219415, + 'water_content_100': 0.175765, 'water_content_500': 0.141025, + 'water_content_1500': 0.118315, }, - 1e-12, - 1e-12, ), ] +CASES_CALC_PTF_CHAKRABORTY2011_EQ2_IDS = [ + 'representative_case', +] -@pytest.mark.parametrize(('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_CHAKRABORTY2011_EQ2) -def test_calc_ptf_chakraborty2011_eq2_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +@pytest.mark.parametrize( + ('inputs', 'expected'), + CASES_CALC_PTF_CHAKRABORTY2011_EQ2, + ids=CASES_CALC_PTF_CHAKRABORTY2011_EQ2_IDS, +) +def test_calc_ptf_chakraborty2011_eq2_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_chakraborty2011_eq2(**inputs) - assert result.water_content_33 == pytest.approx( - expected['water_content_33'], rel=rtol, abs=atol - ) - assert result.water_content_100 == pytest.approx( - expected['water_content_100'], rel=rtol, abs=atol - ) - assert result.water_content_500 == pytest.approx( - expected['water_content_500'], rel=rtol, abs=atol - ) - assert result.water_content_1500 == pytest.approx( - expected['water_content_1500'], rel=rtol, abs=atol + assert_close( + result.water_content_33, + expected['water_content_33'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_100, + expected['water_content_100'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_500, + expected['water_content_500'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_1500, + expected['water_content_1500'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) def test_calc_ptf_chakraborty2011_eq2_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_CHAKRABORTY2011_EQ2, Chakraborty2011PTFResult ) result = calc_ptf_chakraborty2011_eq2(**inputs, out=None) - assert result.water_content_33[0] == pytest.approx( - expected['water_content_33'], rel=rtol, abs=atol - ) - assert result.water_content_100[0] == pytest.approx( - expected['water_content_100'], rel=rtol, abs=atol - ) - assert result.water_content_500[0] == pytest.approx( - expected['water_content_500'], rel=rtol, abs=atol - ) - assert result.water_content_1500[0] == pytest.approx( - expected['water_content_1500'], rel=rtol, abs=atol + assert_close( + result.water_content_33[0], + expected['water_content_33'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_100[0], + expected['water_content_100'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_500[0], + expected['water_content_500'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_1500[0], + expected['water_content_1500'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) def test_calc_ptf_chakraborty2011_eq2_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_CHAKRABORTY2011_EQ2, Chakraborty2011PTFResult ) result = calc_ptf_chakraborty2011_eq2(**inputs, out=out) for actual, expected_out in zip(result, out, strict=True): assert actual is expected_out - assert result.water_content_33[0] == pytest.approx( - expected['water_content_33'], rel=rtol, abs=atol - ) - assert result.water_content_100[0] == pytest.approx( - expected['water_content_100'], rel=rtol, abs=atol - ) - assert result.water_content_500[0] == pytest.approx( - expected['water_content_500'], rel=rtol, abs=atol - ) - assert result.water_content_1500[0] == pytest.approx( - expected['water_content_1500'], rel=rtol, abs=atol + assert_close( + result.water_content_33[0], + expected['water_content_33'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_100[0], + expected['water_content_100'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_500[0], + expected['water_content_500'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_1500[0], + expected['water_content_1500'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) @@ -169,74 +323,151 @@ def test_calc_ptf_chakraborty2011_eq2_out(): ( {'bulk_density': 1.5, 'clay': 20.0, 'silt': 30.0}, { - 'water_content_100': 0.201085, - 'water_content_1500': 0.117865, 'water_content_33': 0.25776, + 'water_content_100': 0.201085, 'water_content_500': 0.139175, + 'water_content_1500': 0.117865, }, - 1e-12, - 1e-12, ), ] +CASES_CALC_PTF_CHAKRABORTY2011_EQ3_IDS = [ + 'representative_case', +] -@pytest.mark.parametrize(('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_CHAKRABORTY2011_EQ3) -def test_calc_ptf_chakraborty2011_eq3_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +@pytest.mark.parametrize( + ('inputs', 'expected'), + CASES_CALC_PTF_CHAKRABORTY2011_EQ3, + ids=CASES_CALC_PTF_CHAKRABORTY2011_EQ3_IDS, +) +def test_calc_ptf_chakraborty2011_eq3_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_chakraborty2011_eq3(**inputs) - assert result.water_content_33 == pytest.approx( - expected['water_content_33'], rel=rtol, abs=atol - ) - assert result.water_content_100 == pytest.approx( - expected['water_content_100'], rel=rtol, abs=atol - ) - assert result.water_content_500 == pytest.approx( - expected['water_content_500'], rel=rtol, abs=atol - ) - assert result.water_content_1500 == pytest.approx( - expected['water_content_1500'], rel=rtol, abs=atol + assert_close( + result.water_content_33, + expected['water_content_33'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_100, + expected['water_content_100'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_500, + expected['water_content_500'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_1500, + expected['water_content_1500'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) def test_calc_ptf_chakraborty2011_eq3_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_CHAKRABORTY2011_EQ3, Chakraborty2011PTFResult ) result = calc_ptf_chakraborty2011_eq3(**inputs, out=None) - assert result.water_content_33[0] == pytest.approx( - expected['water_content_33'], rel=rtol, abs=atol - ) - assert result.water_content_100[0] == pytest.approx( - expected['water_content_100'], rel=rtol, abs=atol - ) - assert result.water_content_500[0] == pytest.approx( - expected['water_content_500'], rel=rtol, abs=atol - ) - assert result.water_content_1500[0] == pytest.approx( - expected['water_content_1500'], rel=rtol, abs=atol + assert_close( + result.water_content_33[0], + expected['water_content_33'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_100[0], + expected['water_content_100'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_500[0], + expected['water_content_500'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_1500[0], + expected['water_content_1500'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) def test_calc_ptf_chakraborty2011_eq3_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_CHAKRABORTY2011_EQ3, Chakraborty2011PTFResult ) result = calc_ptf_chakraborty2011_eq3(**inputs, out=out) for actual, expected_out in zip(result, out, strict=True): assert actual is expected_out - assert result.water_content_33[0] == pytest.approx( - expected['water_content_33'], rel=rtol, abs=atol - ) - assert result.water_content_100[0] == pytest.approx( - expected['water_content_100'], rel=rtol, abs=atol - ) - assert result.water_content_500[0] == pytest.approx( - expected['water_content_500'], rel=rtol, abs=atol - ) - assert result.water_content_1500[0] == pytest.approx( - expected['water_content_1500'], rel=rtol, abs=atol + assert_close( + result.water_content_33[0], + expected['water_content_33'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_100[0], + expected['water_content_100'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_500[0], + expected['water_content_500'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_1500[0], + expected['water_content_1500'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) @@ -244,74 +475,151 @@ def test_calc_ptf_chakraborty2011_eq3_out(): ( {'clay': 20.0, 'sand': 50.0, 'silt': 30.0}, { - 'water_content_100': 0.19235, - 'water_content_1500': 0.11325, 'water_content_33': 0.24397, + 'water_content_100': 0.19235, 'water_content_500': 0.13116, + 'water_content_1500': 0.11325, }, - 1e-12, - 1e-12, ), ] +CASES_CALC_PTF_CHAKRABORTY2011_EQ4_IDS = [ + 'representative_case', +] -@pytest.mark.parametrize(('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_CHAKRABORTY2011_EQ4) -def test_calc_ptf_chakraborty2011_eq4_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +@pytest.mark.parametrize( + ('inputs', 'expected'), + CASES_CALC_PTF_CHAKRABORTY2011_EQ4, + ids=CASES_CALC_PTF_CHAKRABORTY2011_EQ4_IDS, +) +def test_calc_ptf_chakraborty2011_eq4_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_chakraborty2011_eq4(**inputs) - assert result.water_content_33 == pytest.approx( - expected['water_content_33'], rel=rtol, abs=atol - ) - assert result.water_content_100 == pytest.approx( - expected['water_content_100'], rel=rtol, abs=atol - ) - assert result.water_content_500 == pytest.approx( - expected['water_content_500'], rel=rtol, abs=atol - ) - assert result.water_content_1500 == pytest.approx( - expected['water_content_1500'], rel=rtol, abs=atol + assert_close( + result.water_content_33, + expected['water_content_33'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_100, + expected['water_content_100'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_500, + expected['water_content_500'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_1500, + expected['water_content_1500'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) def test_calc_ptf_chakraborty2011_eq4_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_CHAKRABORTY2011_EQ4, Chakraborty2011PTFResult ) result = calc_ptf_chakraborty2011_eq4(**inputs, out=None) - assert result.water_content_33[0] == pytest.approx( - expected['water_content_33'], rel=rtol, abs=atol - ) - assert result.water_content_100[0] == pytest.approx( - expected['water_content_100'], rel=rtol, abs=atol - ) - assert result.water_content_500[0] == pytest.approx( - expected['water_content_500'], rel=rtol, abs=atol - ) - assert result.water_content_1500[0] == pytest.approx( - expected['water_content_1500'], rel=rtol, abs=atol + assert_close( + result.water_content_33[0], + expected['water_content_33'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_100[0], + expected['water_content_100'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_500[0], + expected['water_content_500'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_1500[0], + expected['water_content_1500'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) def test_calc_ptf_chakraborty2011_eq4_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_CHAKRABORTY2011_EQ4, Chakraborty2011PTFResult ) result = calc_ptf_chakraborty2011_eq4(**inputs, out=out) for actual, expected_out in zip(result, out, strict=True): assert actual is expected_out - assert result.water_content_33[0] == pytest.approx( - expected['water_content_33'], rel=rtol, abs=atol - ) - assert result.water_content_100[0] == pytest.approx( - expected['water_content_100'], rel=rtol, abs=atol - ) - assert result.water_content_500[0] == pytest.approx( - expected['water_content_500'], rel=rtol, abs=atol - ) - assert result.water_content_1500[0] == pytest.approx( - expected['water_content_1500'], rel=rtol, abs=atol + assert_close( + result.water_content_33[0], + expected['water_content_33'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_100[0], + expected['water_content_100'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_500[0], + expected['water_content_500'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_1500[0], + expected['water_content_1500'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) @@ -319,74 +627,151 @@ def test_calc_ptf_chakraborty2011_eq4_out(): ( {'bulk_density': 1.5, 'clay': 20.0, 'sand': 50.0, 'silt': 30.0}, { - 'water_content_100': 0.19166, - 'water_content_1500': 0.110595, 'water_content_33': 0.249785, + 'water_content_100': 0.19166, 'water_content_500': 0.131065, + 'water_content_1500': 0.110595, }, - 1e-12, - 1e-12, ), ] +CASES_CALC_PTF_CHAKRABORTY2011_EQ5_IDS = [ + 'representative_case', +] -@pytest.mark.parametrize(('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_CHAKRABORTY2011_EQ5) -def test_calc_ptf_chakraborty2011_eq5_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +@pytest.mark.parametrize( + ('inputs', 'expected'), + CASES_CALC_PTF_CHAKRABORTY2011_EQ5, + ids=CASES_CALC_PTF_CHAKRABORTY2011_EQ5_IDS, +) +def test_calc_ptf_chakraborty2011_eq5_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_chakraborty2011_eq5(**inputs) - assert result.water_content_33 == pytest.approx( - expected['water_content_33'], rel=rtol, abs=atol - ) - assert result.water_content_100 == pytest.approx( - expected['water_content_100'], rel=rtol, abs=atol - ) - assert result.water_content_500 == pytest.approx( - expected['water_content_500'], rel=rtol, abs=atol - ) - assert result.water_content_1500 == pytest.approx( - expected['water_content_1500'], rel=rtol, abs=atol + assert_close( + result.water_content_33, + expected['water_content_33'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_100, + expected['water_content_100'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_500, + expected['water_content_500'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_1500, + expected['water_content_1500'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) def test_calc_ptf_chakraborty2011_eq5_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_CHAKRABORTY2011_EQ5, Chakraborty2011PTFResult ) result = calc_ptf_chakraborty2011_eq5(**inputs, out=None) - assert result.water_content_33[0] == pytest.approx( - expected['water_content_33'], rel=rtol, abs=atol - ) - assert result.water_content_100[0] == pytest.approx( - expected['water_content_100'], rel=rtol, abs=atol - ) - assert result.water_content_500[0] == pytest.approx( - expected['water_content_500'], rel=rtol, abs=atol - ) - assert result.water_content_1500[0] == pytest.approx( - expected['water_content_1500'], rel=rtol, abs=atol + assert_close( + result.water_content_33[0], + expected['water_content_33'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_100[0], + expected['water_content_100'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_500[0], + expected['water_content_500'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_1500[0], + expected['water_content_1500'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) def test_calc_ptf_chakraborty2011_eq5_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_CHAKRABORTY2011_EQ5, Chakraborty2011PTFResult ) result = calc_ptf_chakraborty2011_eq5(**inputs, out=out) for actual, expected_out in zip(result, out, strict=True): assert actual is expected_out - assert result.water_content_33[0] == pytest.approx( - expected['water_content_33'], rel=rtol, abs=atol - ) - assert result.water_content_100[0] == pytest.approx( - expected['water_content_100'], rel=rtol, abs=atol - ) - assert result.water_content_500[0] == pytest.approx( - expected['water_content_500'], rel=rtol, abs=atol - ) - assert result.water_content_1500[0] == pytest.approx( - expected['water_content_1500'], rel=rtol, abs=atol + assert_close( + result.water_content_33[0], + expected['water_content_33'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_100[0], + expected['water_content_100'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_500[0], + expected['water_content_500'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_1500[0], + expected['water_content_1500'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) @@ -394,72 +779,149 @@ def test_calc_ptf_chakraborty2011_eq5_out(): ( {'bulk_density': 1.5, 'clay': 20.0, 'organic_carbon': 0.5, 'sand': 50.0, 'silt': 30.0}, { - 'water_content_100': 0.19695, - 'water_content_1500': 0.113175, 'water_content_33': 0.25207, + 'water_content_100': 0.19695, 'water_content_500': 0.13364, + 'water_content_1500': 0.113175, }, - 1e-12, - 1e-12, ), ] +CASES_CALC_PTF_CHAKRABORTY2011_EQ6_IDS = [ + 'representative_case', +] -@pytest.mark.parametrize(('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_CHAKRABORTY2011_EQ6) -def test_calc_ptf_chakraborty2011_eq6_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +@pytest.mark.parametrize( + ('inputs', 'expected'), + CASES_CALC_PTF_CHAKRABORTY2011_EQ6, + ids=CASES_CALC_PTF_CHAKRABORTY2011_EQ6_IDS, +) +def test_calc_ptf_chakraborty2011_eq6_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_chakraborty2011_eq6(**inputs) - assert result.water_content_33 == pytest.approx( - expected['water_content_33'], rel=rtol, abs=atol - ) - assert result.water_content_100 == pytest.approx( - expected['water_content_100'], rel=rtol, abs=atol - ) - assert result.water_content_500 == pytest.approx( - expected['water_content_500'], rel=rtol, abs=atol - ) - assert result.water_content_1500 == pytest.approx( - expected['water_content_1500'], rel=rtol, abs=atol + assert_close( + result.water_content_33, + expected['water_content_33'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_100, + expected['water_content_100'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_500, + expected['water_content_500'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_1500, + expected['water_content_1500'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) def test_calc_ptf_chakraborty2011_eq6_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_CHAKRABORTY2011_EQ6, Chakraborty2011PTFResult ) result = calc_ptf_chakraborty2011_eq6(**inputs, out=None) - assert result.water_content_33[0] == pytest.approx( - expected['water_content_33'], rel=rtol, abs=atol - ) - assert result.water_content_100[0] == pytest.approx( - expected['water_content_100'], rel=rtol, abs=atol - ) - assert result.water_content_500[0] == pytest.approx( - expected['water_content_500'], rel=rtol, abs=atol - ) - assert result.water_content_1500[0] == pytest.approx( - expected['water_content_1500'], rel=rtol, abs=atol + assert_close( + result.water_content_33[0], + expected['water_content_33'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_100[0], + expected['water_content_100'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_500[0], + expected['water_content_500'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_1500[0], + expected['water_content_1500'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) def test_calc_ptf_chakraborty2011_eq6_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_CHAKRABORTY2011_EQ6, Chakraborty2011PTFResult ) result = calc_ptf_chakraborty2011_eq6(**inputs, out=out) for actual, expected_out in zip(result, out, strict=True): assert actual is expected_out - assert result.water_content_33[0] == pytest.approx( - expected['water_content_33'], rel=rtol, abs=atol - ) - assert result.water_content_100[0] == pytest.approx( - expected['water_content_100'], rel=rtol, abs=atol - ) - assert result.water_content_500[0] == pytest.approx( - expected['water_content_500'], rel=rtol, abs=atol - ) - assert result.water_content_1500[0] == pytest.approx( - expected['water_content_1500'], rel=rtol, abs=atol + assert_close( + result.water_content_33[0], + expected['water_content_33'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_100[0], + expected['water_content_100'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_500[0], + expected['water_content_500'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', + ) + assert_close( + result.water_content_1500[0], + expected['water_content_1500'], + absolute=0.001, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_fraction', + source='registry', ) diff --git a/targets/ptfkit-py/tests/test_clapp1978.py b/targets/ptfkit-py/tests/test_clapp1978.py index 5b3975a..fd67263 100644 --- a/targets/ptfkit-py/tests/test_clapp1978.py +++ b/targets/ptfkit-py/tests/test_clapp1978.py @@ -3,7 +3,7 @@ import pytest -from _helpers import prepare_vector_case +from _helpers import assert_close, prepare_vector_case from ptfkit.clapp1978 import Clapp1978Parameters, UsdaTextureClass, calc_ptf_clapp1978 @@ -12,209 +12,316 @@ {'soil_texture': UsdaTextureClass.SAND}, { 'b': 4.05, - 'saturated_hydraulic_conductivity': 1.056, - 'saturated_water_content': 0.395, 'saturation_suction': 3.5, - 'sorptivity': 1.52, 'wetting_front_suction': 4.66, + 'saturated_water_content': 0.395, + 'saturated_hydraulic_conductivity': 1.056, + 'sorptivity': 1.52, }, - 0.0, - 0.0, ), ( {'soil_texture': UsdaTextureClass.LOAMY_SAND}, { 'b': 4.38, - 'saturated_hydraulic_conductivity': 0.938, - 'saturated_water_content': 0.41, 'saturation_suction': 1.78, - 'sorptivity': 1.04, 'wetting_front_suction': 2.38, + 'saturated_water_content': 0.41, + 'saturated_hydraulic_conductivity': 0.938, + 'sorptivity': 1.04, }, - 0.0, - 0.0, ), ( {'soil_texture': UsdaTextureClass.SANDY_LOAM}, { 'b': 4.9, - 'saturated_hydraulic_conductivity': 0.208, - 'saturated_water_content': 0.435, 'saturation_suction': 7.18, - 'sorptivity': 1.03, 'wetting_front_suction': 9.52, + 'saturated_water_content': 0.435, + 'saturated_hydraulic_conductivity': 0.208, + 'sorptivity': 1.03, }, - 0.0, - 0.0, ), ( {'soil_texture': UsdaTextureClass.SILT_LOAM}, { 'b': 5.3, - 'saturated_hydraulic_conductivity': 0.0432, - 'saturated_water_content': 0.485, 'saturation_suction': 56.6, - 'sorptivity': 1.26, 'wetting_front_suction': 75.3, + 'saturated_water_content': 0.485, + 'saturated_hydraulic_conductivity': 0.0432, + 'sorptivity': 1.26, }, - 0.0, - 0.0, ), ( {'soil_texture': UsdaTextureClass.LOAM}, { 'b': 5.39, - 'saturated_hydraulic_conductivity': 0.0417, - 'saturated_water_content': 0.451, 'saturation_suction': 14.6, - 'sorptivity': 0.693, 'wetting_front_suction': 20.0, + 'saturated_water_content': 0.451, + 'saturated_hydraulic_conductivity': 0.0417, + 'sorptivity': 0.693, }, - 0.0, - 0.0, ), ( {'soil_texture': UsdaTextureClass.SANDY_CLAY_LOAM}, { 'b': 7.12, - 'saturated_hydraulic_conductivity': 0.0378, - 'saturated_water_content': 0.42, 'saturation_suction': 8.63, - 'sorptivity': 0.488, 'wetting_front_suction': 11.7, + 'saturated_water_content': 0.42, + 'saturated_hydraulic_conductivity': 0.0378, + 'sorptivity': 0.488, }, - 0.0, - 0.0, ), ( {'soil_texture': UsdaTextureClass.SILTY_CLAY_LOAM}, { 'b': 7.75, - 'saturated_hydraulic_conductivity': 0.0102, - 'saturated_water_content': 0.477, 'saturation_suction': 14.6, - 'sorptivity': 0.31, 'wetting_front_suction': 19.7, + 'saturated_water_content': 0.477, + 'saturated_hydraulic_conductivity': 0.0102, + 'sorptivity': 0.31, }, - 0.0, - 0.0, ), ( {'soil_texture': UsdaTextureClass.CLAY_LOAM}, { 'b': 8.52, - 'saturated_hydraulic_conductivity': 0.0147, - 'saturated_water_content': 0.476, 'saturation_suction': 36.1, - 'sorptivity': 0.537, 'wetting_front_suction': 48.1, + 'saturated_water_content': 0.476, + 'saturated_hydraulic_conductivity': 0.0147, + 'sorptivity': 0.537, }, - 0.0, - 0.0, ), ( {'soil_texture': UsdaTextureClass.SANDY_CLAY}, { 'b': 10.4, - 'saturated_hydraulic_conductivity': 0.013, - 'saturated_water_content': 0.426, 'saturation_suction': 6.16, - 'sorptivity': 0.223, 'wetting_front_suction': 8.18, + 'saturated_water_content': 0.426, + 'saturated_hydraulic_conductivity': 0.013, + 'sorptivity': 0.223, }, - 0.0, - 0.0, ), ( {'soil_texture': UsdaTextureClass.SILTY_CLAY}, { 'b': 10.4, - 'saturated_hydraulic_conductivity': 0.0062, - 'saturated_water_content': 0.492, 'saturation_suction': 17.4, - 'sorptivity': 0.242, 'wetting_front_suction': 23.0, + 'saturated_water_content': 0.492, + 'saturated_hydraulic_conductivity': 0.0062, + 'sorptivity': 0.242, }, - 0.0, - 0.0, ), ( {'soil_texture': UsdaTextureClass.CLAY}, { 'b': 11.4, - 'saturated_hydraulic_conductivity': 0.0077, - 'saturated_water_content': 0.482, 'saturation_suction': 18.6, - 'sorptivity': 0.268, 'wetting_front_suction': 24.3, + 'saturated_water_content': 0.482, + 'saturated_hydraulic_conductivity': 0.0077, + 'sorptivity': 0.268, }, - 0.0, - 0.0, ), ] +CASES_CALC_PTF_CLAPP1978_IDS = [ + 'table_2_sand', + 'table_2_loamy_sand', + 'table_2_sandy_loam', + 'table_2_silt_loam', + 'table_2_loam', + 'table_2_sandy_clay_loam', + 'table_2_silty_clay_loam', + 'table_2_clay_loam', + 'table_2_sandy_clay', + 'table_2_silty_clay', + 'table_2_clay', +] -@pytest.mark.parametrize(('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_CLAPP1978) -def test_calc_ptf_clapp1978_golden( - inputs: dict[str, object], expected: dict[str, float], rtol: float, atol: float -): +@pytest.mark.parametrize( + ('inputs', 'expected'), CASES_CALC_PTF_CLAPP1978, ids=CASES_CALC_PTF_CLAPP1978_IDS +) +def test_calc_ptf_clapp1978_verification(inputs: dict[str, object], expected: dict[str, float]): result = calc_ptf_clapp1978(**inputs) # ty: ignore[no-matching-overload] - assert result.b == pytest.approx(expected['b'], rel=rtol, abs=atol) - assert result.saturation_suction == pytest.approx( - expected['saturation_suction'], rel=rtol, abs=atol + assert_close( + result.b, + expected['b'], + absolute=0.001, + relative=0.0, + quantity='campbell_b', + unit='dimensionless', + source='registry', + ) + assert_close( + result.saturation_suction, + expected['saturation_suction'], + absolute=0.01, + relative=0.0, + quantity='matric_potential', + unit='centimeter_water_head', + source='registry', + ) + assert_close( + result.wetting_front_suction, + expected['wetting_front_suction'], + absolute=0.01, + relative=0.0, + quantity='matric_potential', + unit='centimeter_water_head', + source='registry', ) - assert result.wetting_front_suction == pytest.approx( - expected['wetting_front_suction'], rel=rtol, abs=atol + assert_close( + result.saturated_water_content, + expected['saturated_water_content'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', ) - assert result.saturated_water_content == pytest.approx( - expected['saturated_water_content'], rel=rtol, abs=atol + assert_close( + result.saturated_hydraulic_conductivity, + expected['saturated_hydraulic_conductivity'], + absolute=1e-5, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='centimeter_per_minute', + source='registry', ) - assert result.saturated_hydraulic_conductivity == pytest.approx( - expected['saturated_hydraulic_conductivity'], rel=rtol, abs=atol + assert_close( + result.sorptivity, + expected['sorptivity'], + absolute=0.0001, + relative=0.01, + quantity='sorptivity', + unit='centimeter_per_square_root_minute', + source='registry', ) - assert result.sorptivity == pytest.approx(expected['sorptivity'], rel=rtol, abs=atol) def test_calc_ptf_clapp1978_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( - CASES_CALC_PTF_CLAPP1978, Clapp1978Parameters - ) + inputs, expected, _out = prepare_vector_case(CASES_CALC_PTF_CLAPP1978, Clapp1978Parameters) result = calc_ptf_clapp1978(**inputs, out=None) - assert result.b[0] == pytest.approx(expected['b'], rel=rtol, abs=atol) - assert result.saturation_suction[0] == pytest.approx( - expected['saturation_suction'], rel=rtol, abs=atol + assert_close( + result.b[0], + expected['b'], + absolute=0.001, + relative=0.0, + quantity='campbell_b', + unit='dimensionless', + source='registry', + ) + assert_close( + result.saturation_suction[0], + expected['saturation_suction'], + absolute=0.01, + relative=0.0, + quantity='matric_potential', + unit='centimeter_water_head', + source='registry', ) - assert result.wetting_front_suction[0] == pytest.approx( - expected['wetting_front_suction'], rel=rtol, abs=atol + assert_close( + result.wetting_front_suction[0], + expected['wetting_front_suction'], + absolute=0.01, + relative=0.0, + quantity='matric_potential', + unit='centimeter_water_head', + source='registry', ) - assert result.saturated_water_content[0] == pytest.approx( - expected['saturated_water_content'], rel=rtol, abs=atol + assert_close( + result.saturated_water_content[0], + expected['saturated_water_content'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', ) - assert result.saturated_hydraulic_conductivity[0] == pytest.approx( - expected['saturated_hydraulic_conductivity'], rel=rtol, abs=atol + assert_close( + result.saturated_hydraulic_conductivity[0], + expected['saturated_hydraulic_conductivity'], + absolute=1e-5, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='centimeter_per_minute', + source='registry', + ) + assert_close( + result.sorptivity[0], + expected['sorptivity'], + absolute=0.0001, + relative=0.01, + quantity='sorptivity', + unit='centimeter_per_square_root_minute', + source='registry', ) - assert result.sorptivity[0] == pytest.approx(expected['sorptivity'], rel=rtol, abs=atol) def test_calc_ptf_clapp1978_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( - CASES_CALC_PTF_CLAPP1978, Clapp1978Parameters - ) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_CLAPP1978, Clapp1978Parameters) result = calc_ptf_clapp1978(**inputs, out=out) for actual, expected_out in zip(result, out, strict=True): assert actual is expected_out - assert result.b[0] == pytest.approx(expected['b'], rel=rtol, abs=atol) - assert result.saturation_suction[0] == pytest.approx( - expected['saturation_suction'], rel=rtol, abs=atol + assert_close( + result.b[0], + expected['b'], + absolute=0.001, + relative=0.0, + quantity='campbell_b', + unit='dimensionless', + source='registry', + ) + assert_close( + result.saturation_suction[0], + expected['saturation_suction'], + absolute=0.01, + relative=0.0, + quantity='matric_potential', + unit='centimeter_water_head', + source='registry', + ) + assert_close( + result.wetting_front_suction[0], + expected['wetting_front_suction'], + absolute=0.01, + relative=0.0, + quantity='matric_potential', + unit='centimeter_water_head', + source='registry', ) - assert result.wetting_front_suction[0] == pytest.approx( - expected['wetting_front_suction'], rel=rtol, abs=atol + assert_close( + result.saturated_water_content[0], + expected['saturated_water_content'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', ) - assert result.saturated_water_content[0] == pytest.approx( - expected['saturated_water_content'], rel=rtol, abs=atol + assert_close( + result.saturated_hydraulic_conductivity[0], + expected['saturated_hydraulic_conductivity'], + absolute=1e-5, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='centimeter_per_minute', + source='registry', ) - assert result.saturated_hydraulic_conductivity[0] == pytest.approx( - expected['saturated_hydraulic_conductivity'], rel=rtol, abs=atol + assert_close( + result.sorptivity[0], + expected['sorptivity'], + absolute=0.0001, + relative=0.01, + quantity='sorptivity', + unit='centimeter_per_square_root_minute', + source='registry', ) - assert result.sorptivity[0] == pytest.approx(expected['sorptivity'], rel=rtol, abs=atol) diff --git a/targets/ptfkit-py/tests/test_comparator.py b/targets/ptfkit-py/tests/test_comparator.py new file mode 100644 index 0000000..c6eae43 --- /dev/null +++ b/targets/ptfkit-py/tests/test_comparator.py @@ -0,0 +1,22 @@ +# @generated by ptfkit-codegen; DO NOT EDIT. + +import pytest + +from _helpers import assert_close, resolved_tolerance + + +@pytest.mark.parametrize('expected', [0.0, 2.0, -2.0]) +def test_accepts_below_and_rejects_above_tolerance(expected: float): + absolute = 0.001 + relative = 0.01 + tolerance = resolved_tolerance(expected, absolute, relative) + metadata = { + 'absolute': absolute, + 'relative': relative, + 'quantity': 'test_quantity', + 'unit': '1', + 'source': 'registry', + } + assert_close(expected + tolerance * 0.5, expected, **metadata) + with pytest.raises(AssertionError): + assert_close(expected + tolerance * 2.0, expected, **metadata) diff --git a/targets/ptfkit-py/tests/test_cosby1984.py b/targets/ptfkit-py/tests/test_cosby1984.py index 9c6cf96..79dc0dd 100644 --- a/targets/ptfkit-py/tests/test_cosby1984.py +++ b/targets/ptfkit-py/tests/test_cosby1984.py @@ -3,7 +3,7 @@ import pytest -from _helpers import prepare_vector_case +from _helpers import assert_close, prepare_vector_case from ptfkit.cosby1984 import Cosby1984UnivariatePTFResult, calc_ptf_cosby1984_univariate @@ -12,75 +12,245 @@ {'clay': 20.0, 'sand': 50.0, 'silt': 30.0}, { 'mean_b': 6.09, - 'mean_log_k_sat': -0.119, 'mean_log_psi_s': 1.225, + 'mean_log_k_sat': -0.119, 'mean_theta_s': 42.6, 'sd_b': 2.34, 'sd_log_k_sat': 0.5553, 'sd_theta_s': 6.27, }, - 1e-12, - 1e-12, ), ( {'clay': 5.0, 'sand': 80.0, 'silt': 15.0}, { 'mean_b': 3.705, - 'mean_log_k_sat': 0.34, 'mean_log_psi_s': 0.832, + 'mean_log_k_sat': 0.34, 'mean_theta_s': 38.82, 'sd_b': 1.59, 'sd_log_k_sat': 0.50715, 'sd_theta_s': 7.365, }, - 1e-12, - 1e-12, ), ] +CASES_CALC_PTF_COSBY1984_UNIVARIATE_IDS = [ + 'scalar_mid_texture', + 'scalar_sandy_texture', +] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_COSBY1984_UNIVARIATE + ('inputs', 'expected'), + CASES_CALC_PTF_COSBY1984_UNIVARIATE, + ids=CASES_CALC_PTF_COSBY1984_UNIVARIATE_IDS, ) -def test_calc_ptf_cosby1984_univariate_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_cosby1984_univariate_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_cosby1984_univariate(**inputs) - assert result.mean_b == pytest.approx(expected['mean_b'], rel=rtol, abs=atol) - assert result.mean_log_psi_s == pytest.approx(expected['mean_log_psi_s'], rel=rtol, abs=atol) - assert result.mean_log_k_sat == pytest.approx(expected['mean_log_k_sat'], rel=rtol, abs=atol) - assert result.mean_theta_s == pytest.approx(expected['mean_theta_s'], rel=rtol, abs=atol) - assert result.sd_b == pytest.approx(expected['sd_b'], rel=rtol, abs=atol) - assert result.sd_log_k_sat == pytest.approx(expected['sd_log_k_sat'], rel=rtol, abs=atol) - assert result.sd_theta_s == pytest.approx(expected['sd_theta_s'], rel=rtol, abs=atol) + assert_close( + result.mean_b, + expected['mean_b'], + absolute=0.001, + relative=0.0, + quantity='campbell_b', + unit='dimensionless', + source='registry', + ) + assert_close( + result.mean_log_psi_s, + expected['mean_log_psi_s'], + absolute=0.001, + relative=0.0, + quantity='log_saturation_potential', + unit='reported_log_value', + source='registry', + ) + assert_close( + result.mean_log_k_sat, + expected['mean_log_k_sat'], + absolute=0.001, + relative=0.0, + quantity='log_saturated_hydraulic_conductivity', + unit='reported_log_value', + source='registry', + ) + assert_close( + result.mean_theta_s, + expected['mean_theta_s'], + absolute=0.1, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_percent', + source='registry', + ) + assert_close( + result.sd_b, + expected['sd_b'], + absolute=0.001, + relative=0.0, + quantity='campbell_b', + unit='dimensionless', + source='registry', + ) + assert_close( + result.sd_log_k_sat, + expected['sd_log_k_sat'], + absolute=0.001, + relative=0.0, + quantity='log_saturated_hydraulic_conductivity', + unit='reported_log_value', + source='registry', + ) + assert_close( + result.sd_theta_s, + expected['sd_theta_s'], + absolute=0.1, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_percent', + source='registry', + ) def test_calc_ptf_cosby1984_univariate_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_COSBY1984_UNIVARIATE, Cosby1984UnivariatePTFResult ) result = calc_ptf_cosby1984_univariate(**inputs, out=None) - assert result.mean_b[0] == pytest.approx(expected['mean_b'], rel=rtol, abs=atol) - assert result.mean_log_psi_s[0] == pytest.approx(expected['mean_log_psi_s'], rel=rtol, abs=atol) - assert result.mean_log_k_sat[0] == pytest.approx(expected['mean_log_k_sat'], rel=rtol, abs=atol) - assert result.mean_theta_s[0] == pytest.approx(expected['mean_theta_s'], rel=rtol, abs=atol) - assert result.sd_b[0] == pytest.approx(expected['sd_b'], rel=rtol, abs=atol) - assert result.sd_log_k_sat[0] == pytest.approx(expected['sd_log_k_sat'], rel=rtol, abs=atol) - assert result.sd_theta_s[0] == pytest.approx(expected['sd_theta_s'], rel=rtol, abs=atol) + assert_close( + result.mean_b[0], + expected['mean_b'], + absolute=0.001, + relative=0.0, + quantity='campbell_b', + unit='dimensionless', + source='registry', + ) + assert_close( + result.mean_log_psi_s[0], + expected['mean_log_psi_s'], + absolute=0.001, + relative=0.0, + quantity='log_saturation_potential', + unit='reported_log_value', + source='registry', + ) + assert_close( + result.mean_log_k_sat[0], + expected['mean_log_k_sat'], + absolute=0.001, + relative=0.0, + quantity='log_saturated_hydraulic_conductivity', + unit='reported_log_value', + source='registry', + ) + assert_close( + result.mean_theta_s[0], + expected['mean_theta_s'], + absolute=0.1, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_percent', + source='registry', + ) + assert_close( + result.sd_b[0], + expected['sd_b'], + absolute=0.001, + relative=0.0, + quantity='campbell_b', + unit='dimensionless', + source='registry', + ) + assert_close( + result.sd_log_k_sat[0], + expected['sd_log_k_sat'], + absolute=0.001, + relative=0.0, + quantity='log_saturated_hydraulic_conductivity', + unit='reported_log_value', + source='registry', + ) + assert_close( + result.sd_theta_s[0], + expected['sd_theta_s'], + absolute=0.1, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_percent', + source='registry', + ) def test_calc_ptf_cosby1984_univariate_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_COSBY1984_UNIVARIATE, Cosby1984UnivariatePTFResult ) result = calc_ptf_cosby1984_univariate(**inputs, out=out) for actual, expected_out in zip(result, out, strict=True): assert actual is expected_out - assert result.mean_b[0] == pytest.approx(expected['mean_b'], rel=rtol, abs=atol) - assert result.mean_log_psi_s[0] == pytest.approx(expected['mean_log_psi_s'], rel=rtol, abs=atol) - assert result.mean_log_k_sat[0] == pytest.approx(expected['mean_log_k_sat'], rel=rtol, abs=atol) - assert result.mean_theta_s[0] == pytest.approx(expected['mean_theta_s'], rel=rtol, abs=atol) - assert result.sd_b[0] == pytest.approx(expected['sd_b'], rel=rtol, abs=atol) - assert result.sd_log_k_sat[0] == pytest.approx(expected['sd_log_k_sat'], rel=rtol, abs=atol) - assert result.sd_theta_s[0] == pytest.approx(expected['sd_theta_s'], rel=rtol, abs=atol) + assert_close( + result.mean_b[0], + expected['mean_b'], + absolute=0.001, + relative=0.0, + quantity='campbell_b', + unit='dimensionless', + source='registry', + ) + assert_close( + result.mean_log_psi_s[0], + expected['mean_log_psi_s'], + absolute=0.001, + relative=0.0, + quantity='log_saturation_potential', + unit='reported_log_value', + source='registry', + ) + assert_close( + result.mean_log_k_sat[0], + expected['mean_log_k_sat'], + absolute=0.001, + relative=0.0, + quantity='log_saturated_hydraulic_conductivity', + unit='reported_log_value', + source='registry', + ) + assert_close( + result.mean_theta_s[0], + expected['mean_theta_s'], + absolute=0.1, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_percent', + source='registry', + ) + assert_close( + result.sd_b[0], + expected['sd_b'], + absolute=0.001, + relative=0.0, + quantity='campbell_b', + unit='dimensionless', + source='registry', + ) + assert_close( + result.sd_log_k_sat[0], + expected['sd_log_k_sat'], + absolute=0.001, + relative=0.0, + quantity='log_saturated_hydraulic_conductivity', + unit='reported_log_value', + source='registry', + ) + assert_close( + result.sd_theta_s[0], + expected['sd_theta_s'], + absolute=0.1, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_percent', + source='registry', + ) diff --git a/targets/ptfkit-py/tests/test_dharumarajan2019.py b/targets/ptfkit-py/tests/test_dharumarajan2019.py index 7dfea55..7822301 100644 --- a/targets/ptfkit-py/tests/test_dharumarajan2019.py +++ b/targets/ptfkit-py/tests/test_dharumarajan2019.py @@ -3,7 +3,7 @@ import pytest -from _helpers import prepare_vector_case +from _helpers import assert_close, prepare_vector_case from ptfkit.dharumarajan2019 import ( Dharumarajan2019WaterRetentionResult, calc_ptf_dharumarajan2019_infiltration, @@ -18,90 +18,182 @@ ( {'cation_exchange_capacity': 33.6, 'clay': 43.2, 'sand': 39.8}, {'field_capacity': 29.7912, 'permanent_wilting_point': 18.9276}, - 1e-12, - 1e-12, ), ] +CASES_CALC_PTF_DHARUMARAJAN2019_NKP_IDS = [ + 'northern_study_mean_case', +] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_DHARUMARAJAN2019_NKP + ('inputs', 'expected'), + CASES_CALC_PTF_DHARUMARAJAN2019_NKP, + ids=CASES_CALC_PTF_DHARUMARAJAN2019_NKP_IDS, ) -def test_calc_ptf_dharumarajan2019_nkp_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_dharumarajan2019_nkp_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_dharumarajan2019_nkp(**inputs) - assert result.field_capacity == pytest.approx(expected['field_capacity'], rel=rtol, abs=atol) - assert result.permanent_wilting_point == pytest.approx( - expected['permanent_wilting_point'], rel=rtol, abs=atol + assert_close( + result.field_capacity, + expected['field_capacity'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) + assert_close( + result.permanent_wilting_point, + expected['permanent_wilting_point'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', ) def test_calc_ptf_dharumarajan2019_nkp_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_DHARUMARAJAN2019_NKP, Dharumarajan2019WaterRetentionResult ) result = calc_ptf_dharumarajan2019_nkp(**inputs, out=None) - assert result.field_capacity[0] == pytest.approx(expected['field_capacity'], rel=rtol, abs=atol) - assert result.permanent_wilting_point[0] == pytest.approx( - expected['permanent_wilting_point'], rel=rtol, abs=atol + assert_close( + result.field_capacity[0], + expected['field_capacity'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) + assert_close( + result.permanent_wilting_point[0], + expected['permanent_wilting_point'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', ) def test_calc_ptf_dharumarajan2019_nkp_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_DHARUMARAJAN2019_NKP, Dharumarajan2019WaterRetentionResult ) result = calc_ptf_dharumarajan2019_nkp(**inputs, out=out) for actual, expected_out in zip(result, out, strict=True): assert actual is expected_out - assert result.field_capacity[0] == pytest.approx(expected['field_capacity'], rel=rtol, abs=atol) - assert result.permanent_wilting_point[0] == pytest.approx( - expected['permanent_wilting_point'], rel=rtol, abs=atol + assert_close( + result.field_capacity[0], + expected['field_capacity'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) + assert_close( + result.permanent_wilting_point[0], + expected['permanent_wilting_point'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', ) CASES_CALC_PTF_DHARUMARAJAN2019_NKP_CLAY = [ - ({'clay': 43.2}, {'field_capacity': 30.2832, 'permanent_wilting_point': 19.157}, 1e-12, 1e-12), + ({'clay': 43.2}, {'field_capacity': 30.2832, 'permanent_wilting_point': 19.157}), +] +CASES_CALC_PTF_DHARUMARAJAN2019_NKP_CLAY_IDS = [ + 'northern_study_mean_clay_case', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_DHARUMARAJAN2019_NKP_CLAY + ('inputs', 'expected'), + CASES_CALC_PTF_DHARUMARAJAN2019_NKP_CLAY, + ids=CASES_CALC_PTF_DHARUMARAJAN2019_NKP_CLAY_IDS, ) -def test_calc_ptf_dharumarajan2019_nkp_clay_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_dharumarajan2019_nkp_clay_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_dharumarajan2019_nkp_clay(**inputs) - assert result.field_capacity == pytest.approx(expected['field_capacity'], rel=rtol, abs=atol) - assert result.permanent_wilting_point == pytest.approx( - expected['permanent_wilting_point'], rel=rtol, abs=atol + assert_close( + result.field_capacity, + expected['field_capacity'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) + assert_close( + result.permanent_wilting_point, + expected['permanent_wilting_point'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', ) def test_calc_ptf_dharumarajan2019_nkp_clay_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_DHARUMARAJAN2019_NKP_CLAY, Dharumarajan2019WaterRetentionResult ) result = calc_ptf_dharumarajan2019_nkp_clay(**inputs, out=None) - assert result.field_capacity[0] == pytest.approx(expected['field_capacity'], rel=rtol, abs=atol) - assert result.permanent_wilting_point[0] == pytest.approx( - expected['permanent_wilting_point'], rel=rtol, abs=atol + assert_close( + result.field_capacity[0], + expected['field_capacity'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) + assert_close( + result.permanent_wilting_point[0], + expected['permanent_wilting_point'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', ) def test_calc_ptf_dharumarajan2019_nkp_clay_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_DHARUMARAJAN2019_NKP_CLAY, Dharumarajan2019WaterRetentionResult ) result = calc_ptf_dharumarajan2019_nkp_clay(**inputs, out=out) for actual, expected_out in zip(result, out, strict=True): assert actual is expected_out - assert result.field_capacity[0] == pytest.approx(expected['field_capacity'], rel=rtol, abs=atol) - assert result.permanent_wilting_point[0] == pytest.approx( - expected['permanent_wilting_point'], rel=rtol, abs=atol + assert_close( + result.field_capacity[0], + expected['field_capacity'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) + assert_close( + result.permanent_wilting_point[0], + expected['permanent_wilting_point'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', ) @@ -109,121 +201,238 @@ def test_calc_ptf_dharumarajan2019_nkp_clay_out(): ( {'cation_exchange_capacity': 14.7, 'clay': 31.5, 'sand': 53.5}, {'field_capacity': 21.8169, 'permanent_wilting_point': 11.3054}, - 1e-12, - 1e-12, ), ] +CASES_CALC_PTF_DHARUMARAJAN2019_SKP_IDS = [ + 'southern_study_mean_case', +] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_DHARUMARAJAN2019_SKP + ('inputs', 'expected'), + CASES_CALC_PTF_DHARUMARAJAN2019_SKP, + ids=CASES_CALC_PTF_DHARUMARAJAN2019_SKP_IDS, ) -def test_calc_ptf_dharumarajan2019_skp_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_dharumarajan2019_skp_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_dharumarajan2019_skp(**inputs) - assert result.field_capacity == pytest.approx(expected['field_capacity'], rel=rtol, abs=atol) - assert result.permanent_wilting_point == pytest.approx( - expected['permanent_wilting_point'], rel=rtol, abs=atol + assert_close( + result.field_capacity, + expected['field_capacity'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) + assert_close( + result.permanent_wilting_point, + expected['permanent_wilting_point'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', ) def test_calc_ptf_dharumarajan2019_skp_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_DHARUMARAJAN2019_SKP, Dharumarajan2019WaterRetentionResult ) result = calc_ptf_dharumarajan2019_skp(**inputs, out=None) - assert result.field_capacity[0] == pytest.approx(expected['field_capacity'], rel=rtol, abs=atol) - assert result.permanent_wilting_point[0] == pytest.approx( - expected['permanent_wilting_point'], rel=rtol, abs=atol + assert_close( + result.field_capacity[0], + expected['field_capacity'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) + assert_close( + result.permanent_wilting_point[0], + expected['permanent_wilting_point'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', ) def test_calc_ptf_dharumarajan2019_skp_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_DHARUMARAJAN2019_SKP, Dharumarajan2019WaterRetentionResult ) result = calc_ptf_dharumarajan2019_skp(**inputs, out=out) for actual, expected_out in zip(result, out, strict=True): assert actual is expected_out - assert result.field_capacity[0] == pytest.approx(expected['field_capacity'], rel=rtol, abs=atol) - assert result.permanent_wilting_point[0] == pytest.approx( - expected['permanent_wilting_point'], rel=rtol, abs=atol + assert_close( + result.field_capacity[0], + expected['field_capacity'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) + assert_close( + result.permanent_wilting_point[0], + expected['permanent_wilting_point'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', ) CASES_CALC_PTF_DHARUMARAJAN2019_SKP_CLAY = [ - ({'clay': 31.5}, {'field_capacity': 22.0255, 'permanent_wilting_point': 11.503}, 1e-12, 1e-12), + ({'clay': 31.5}, {'field_capacity': 22.0255, 'permanent_wilting_point': 11.503}), +] +CASES_CALC_PTF_DHARUMARAJAN2019_SKP_CLAY_IDS = [ + 'southern_study_mean_clay_case', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_DHARUMARAJAN2019_SKP_CLAY + ('inputs', 'expected'), + CASES_CALC_PTF_DHARUMARAJAN2019_SKP_CLAY, + ids=CASES_CALC_PTF_DHARUMARAJAN2019_SKP_CLAY_IDS, ) -def test_calc_ptf_dharumarajan2019_skp_clay_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_dharumarajan2019_skp_clay_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_dharumarajan2019_skp_clay(**inputs) - assert result.field_capacity == pytest.approx(expected['field_capacity'], rel=rtol, abs=atol) - assert result.permanent_wilting_point == pytest.approx( - expected['permanent_wilting_point'], rel=rtol, abs=atol + assert_close( + result.field_capacity, + expected['field_capacity'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) + assert_close( + result.permanent_wilting_point, + expected['permanent_wilting_point'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', ) def test_calc_ptf_dharumarajan2019_skp_clay_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_DHARUMARAJAN2019_SKP_CLAY, Dharumarajan2019WaterRetentionResult ) result = calc_ptf_dharumarajan2019_skp_clay(**inputs, out=None) - assert result.field_capacity[0] == pytest.approx(expected['field_capacity'], rel=rtol, abs=atol) - assert result.permanent_wilting_point[0] == pytest.approx( - expected['permanent_wilting_point'], rel=rtol, abs=atol + assert_close( + result.field_capacity[0], + expected['field_capacity'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) + assert_close( + result.permanent_wilting_point[0], + expected['permanent_wilting_point'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', ) def test_calc_ptf_dharumarajan2019_skp_clay_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_DHARUMARAJAN2019_SKP_CLAY, Dharumarajan2019WaterRetentionResult ) result = calc_ptf_dharumarajan2019_skp_clay(**inputs, out=out) for actual, expected_out in zip(result, out, strict=True): assert actual is expected_out - assert result.field_capacity[0] == pytest.approx(expected['field_capacity'], rel=rtol, abs=atol) - assert result.permanent_wilting_point[0] == pytest.approx( - expected['permanent_wilting_point'], rel=rtol, abs=atol + assert_close( + result.field_capacity[0], + expected['field_capacity'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) + assert_close( + result.permanent_wilting_point[0], + expected['permanent_wilting_point'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', ) CASES_CALC_PTF_DHARUMARAJAN2019_INFILTRATION = [ - ({'clay': 30.0, 'sand': 50.0, 'silt': 20.0}, {'infiltration_rate': 18.45}, 1e-12, 1e-12), + ({'clay': 30.0, 'sand': 50.0, 'silt': 20.0}, {'infiltration_rate': 18.45}), +] +CASES_CALC_PTF_DHARUMARAJAN2019_INFILTRATION_IDS = [ + 'balanced_texture_case', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_DHARUMARAJAN2019_INFILTRATION + ('inputs', 'expected'), + CASES_CALC_PTF_DHARUMARAJAN2019_INFILTRATION, + ids=CASES_CALC_PTF_DHARUMARAJAN2019_INFILTRATION_IDS, ) -def test_calc_ptf_dharumarajan2019_infiltration_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_dharumarajan2019_infiltration_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_dharumarajan2019_infiltration(**inputs) - assert result == pytest.approx(expected['infiltration_rate'], rel=rtol, abs=atol) + assert_close( + result, + expected['infiltration_rate'], + absolute=0.0001, + relative=0.01, + quantity='infiltration_rate', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_dharumarajan2019_infiltration_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( - CASES_CALC_PTF_DHARUMARAJAN2019_INFILTRATION - ) + inputs, expected, _out = prepare_vector_case(CASES_CALC_PTF_DHARUMARAJAN2019_INFILTRATION) result = calc_ptf_dharumarajan2019_infiltration(**inputs, out=None) - assert result[0] == pytest.approx(expected['infiltration_rate'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['infiltration_rate'], + absolute=0.0001, + relative=0.01, + quantity='infiltration_rate', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_dharumarajan2019_infiltration_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( - CASES_CALC_PTF_DHARUMARAJAN2019_INFILTRATION - ) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_DHARUMARAJAN2019_INFILTRATION) result = calc_ptf_dharumarajan2019_infiltration(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['infiltration_rate'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['infiltration_rate'], + absolute=0.0001, + relative=0.01, + quantity='infiltration_rate', + unit='millimeter_per_hour', + source='registry', + ) diff --git a/targets/ptfkit-py/tests/test_ferrerjulia2004.py b/targets/ptfkit-py/tests/test_ferrerjulia2004.py index 02edd16..977210b 100644 --- a/targets/ptfkit-py/tests/test_ferrerjulia2004.py +++ b/targets/ptfkit-py/tests/test_ferrerjulia2004.py @@ -3,7 +3,7 @@ import pytest -from _helpers import prepare_vector_case +from _helpers import assert_close, prepare_vector_case from ptfkit.ferrerjulia2004 import ( calc_ptf_ferrerjulia2004_calcaric_fluvisol_sand, calc_ptf_ferrerjulia2004_calcaric_fluvisol_texture_organic_matter, @@ -50,1359 +50,2476 @@ CASES_CALC_PTF_FERRERJULIA2004_CAMPBELL_SHIOZAWA = [ - ({'clay': 25.0, 'sand': 50.0}, {'k_sat': 0.025071690110308933}, 1e-12, 1e-12), + ({'clay': 25.0, 'sand': 50.0}, {'k_sat': 0.025071690110308933}), +] +CASES_CALC_PTF_FERRERJULIA2004_CAMPBELL_SHIOZAWA_IDS = [ + 'reviewer_reference_vector', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_FERRERJULIA2004_CAMPBELL_SHIOZAWA + ('inputs', 'expected'), + CASES_CALC_PTF_FERRERJULIA2004_CAMPBELL_SHIOZAWA, + ids=CASES_CALC_PTF_FERRERJULIA2004_CAMPBELL_SHIOZAWA_IDS, ) -def test_calc_ptf_ferrerjulia2004_campbell_shiozawa_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_ferrerjulia2004_campbell_shiozawa_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_ferrerjulia2004_campbell_shiozawa(**inputs) - assert result == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result, + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_campbell_shiozawa_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( - CASES_CALC_PTF_FERRERJULIA2004_CAMPBELL_SHIOZAWA - ) + inputs, expected, _out = prepare_vector_case(CASES_CALC_PTF_FERRERJULIA2004_CAMPBELL_SHIOZAWA) result = calc_ptf_ferrerjulia2004_campbell_shiozawa(**inputs, out=None) - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_campbell_shiozawa_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( - CASES_CALC_PTF_FERRERJULIA2004_CAMPBELL_SHIOZAWA - ) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_FERRERJULIA2004_CAMPBELL_SHIOZAWA) result = calc_ptf_ferrerjulia2004_campbell_shiozawa(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) CASES_CALC_PTF_FERRERJULIA2004_SAXTON = [ - ({'clay': 25.0, 'sand': 50.0}, {'k_sat': 0.00022673430784327228}, 1e-12, 1e-12), + ({'clay': 25.0, 'sand': 50.0}, {'k_sat': 0.00022673430784327228}), +] +CASES_CALC_PTF_FERRERJULIA2004_SAXTON_IDS = [ + 'reviewer_reference_vector', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_FERRERJULIA2004_SAXTON + ('inputs', 'expected'), + CASES_CALC_PTF_FERRERJULIA2004_SAXTON, + ids=CASES_CALC_PTF_FERRERJULIA2004_SAXTON_IDS, ) -def test_calc_ptf_ferrerjulia2004_saxton_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_ferrerjulia2004_saxton_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_ferrerjulia2004_saxton(**inputs) - assert result == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result, + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_saxton_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case(CASES_CALC_PTF_FERRERJULIA2004_SAXTON) + inputs, expected, _out = prepare_vector_case(CASES_CALC_PTF_FERRERJULIA2004_SAXTON) result = calc_ptf_ferrerjulia2004_saxton(**inputs, out=None) - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_saxton_out(): - inputs, expected, rtol, atol, out = prepare_vector_case(CASES_CALC_PTF_FERRERJULIA2004_SAXTON) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_FERRERJULIA2004_SAXTON) result = calc_ptf_ferrerjulia2004_saxton(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) CASES_CALC_PTF_FERRERJULIA2004_DANE_PUCKETT = [ - ({'clay': 25.0}, {'k_sat': 8.302039828385373}, 1e-12, 1e-12), + ({'clay': 25.0}, {'k_sat': 8.302039828385373}), +] +CASES_CALC_PTF_FERRERJULIA2004_DANE_PUCKETT_IDS = [ + 'reviewer_reference_vector', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_FERRERJULIA2004_DANE_PUCKETT + ('inputs', 'expected'), + CASES_CALC_PTF_FERRERJULIA2004_DANE_PUCKETT, + ids=CASES_CALC_PTF_FERRERJULIA2004_DANE_PUCKETT_IDS, ) -def test_calc_ptf_ferrerjulia2004_dane_puckett_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_ferrerjulia2004_dane_puckett_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_ferrerjulia2004_dane_puckett(**inputs) - assert result == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result, + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_dane_puckett_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( - CASES_CALC_PTF_FERRERJULIA2004_DANE_PUCKETT - ) + inputs, expected, _out = prepare_vector_case(CASES_CALC_PTF_FERRERJULIA2004_DANE_PUCKETT) result = calc_ptf_ferrerjulia2004_dane_puckett(**inputs, out=None) - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_dane_puckett_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( - CASES_CALC_PTF_FERRERJULIA2004_DANE_PUCKETT - ) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_FERRERJULIA2004_DANE_PUCKETT) result = calc_ptf_ferrerjulia2004_dane_puckett(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) CASES_CALC_PTF_FERRERJULIA2004_PUCKETT = [ - ({'clay': 25.0}, {'k_sat': 1.1257967371765654}, 1e-12, 1e-12), + ({'clay': 25.0}, {'k_sat': 1.1257967371765654}), +] +CASES_CALC_PTF_FERRERJULIA2004_PUCKETT_IDS = [ + 'reviewer_reference_vector', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_FERRERJULIA2004_PUCKETT + ('inputs', 'expected'), + CASES_CALC_PTF_FERRERJULIA2004_PUCKETT, + ids=CASES_CALC_PTF_FERRERJULIA2004_PUCKETT_IDS, ) -def test_calc_ptf_ferrerjulia2004_puckett_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_ferrerjulia2004_puckett_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_ferrerjulia2004_puckett(**inputs) - assert result == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result, + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_puckett_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case(CASES_CALC_PTF_FERRERJULIA2004_PUCKETT) + inputs, expected, _out = prepare_vector_case(CASES_CALC_PTF_FERRERJULIA2004_PUCKETT) result = calc_ptf_ferrerjulia2004_puckett(**inputs, out=None) - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_puckett_out(): - inputs, expected, rtol, atol, out = prepare_vector_case(CASES_CALC_PTF_FERRERJULIA2004_PUCKETT) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_FERRERJULIA2004_PUCKETT) result = calc_ptf_ferrerjulia2004_puckett(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) CASES_CALC_PTF_FERRERJULIA2004_COSBY = [ - ({'clay': 25.0, 'sand': 50.0}, {'k_sat': 26.091830970918934}, 1e-12, 1e-12), + ({'clay': 25.0, 'sand': 50.0}, {'k_sat': 26.091830970918934}), +] +CASES_CALC_PTF_FERRERJULIA2004_COSBY_IDS = [ + 'reviewer_reference_vector', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_FERRERJULIA2004_COSBY + ('inputs', 'expected'), + CASES_CALC_PTF_FERRERJULIA2004_COSBY, + ids=CASES_CALC_PTF_FERRERJULIA2004_COSBY_IDS, ) -def test_calc_ptf_ferrerjulia2004_cosby_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_ferrerjulia2004_cosby_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_ferrerjulia2004_cosby(**inputs) - assert result == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result, + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_cosby_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case(CASES_CALC_PTF_FERRERJULIA2004_COSBY) + inputs, expected, _out = prepare_vector_case(CASES_CALC_PTF_FERRERJULIA2004_COSBY) result = calc_ptf_ferrerjulia2004_cosby(**inputs, out=None) - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_cosby_out(): - inputs, expected, rtol, atol, out = prepare_vector_case(CASES_CALC_PTF_FERRERJULIA2004_COSBY) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_FERRERJULIA2004_COSBY) result = calc_ptf_ferrerjulia2004_cosby(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) CASES_CALC_PTF_FERRERJULIA2004_HUMIC_ACRISOL_SAND = [ - ({'sand': 50.0}, {'k_sat': 14.529879794648146}, 1e-12, 1e-12), + ({'sand': 50.0}, {'k_sat': 14.529879794648146}), +] +CASES_CALC_PTF_FERRERJULIA2004_HUMIC_ACRISOL_SAND_IDS = [ + 'reviewer_reference_vector', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_FERRERJULIA2004_HUMIC_ACRISOL_SAND + ('inputs', 'expected'), + CASES_CALC_PTF_FERRERJULIA2004_HUMIC_ACRISOL_SAND, + ids=CASES_CALC_PTF_FERRERJULIA2004_HUMIC_ACRISOL_SAND_IDS, ) -def test_calc_ptf_ferrerjulia2004_humic_acrisol_sand_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_ferrerjulia2004_humic_acrisol_sand_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_ferrerjulia2004_humic_acrisol_sand(**inputs) - assert result == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result, + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_humic_acrisol_sand_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( - CASES_CALC_PTF_FERRERJULIA2004_HUMIC_ACRISOL_SAND - ) + inputs, expected, _out = prepare_vector_case(CASES_CALC_PTF_FERRERJULIA2004_HUMIC_ACRISOL_SAND) result = calc_ptf_ferrerjulia2004_humic_acrisol_sand(**inputs, out=None) - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_humic_acrisol_sand_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( - CASES_CALC_PTF_FERRERJULIA2004_HUMIC_ACRISOL_SAND - ) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_FERRERJULIA2004_HUMIC_ACRISOL_SAND) result = calc_ptf_ferrerjulia2004_humic_acrisol_sand(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) CASES_CALC_PTF_FERRERJULIA2004_HUMIC_ACRISOL_TEXTURE_ORGANIC_MATTER = [ - ({'clay': 25.0, 'organic_matter': 2.5, 'sand': 50.0}, {'k_sat': 19.563475}, 1e-12, 1e-12), + ({'clay': 25.0, 'organic_matter': 2.5, 'sand': 50.0}, {'k_sat': 19.563475}), +] +CASES_CALC_PTF_FERRERJULIA2004_HUMIC_ACRISOL_TEXTURE_ORGANIC_MATTER_IDS = [ + 'reviewer_reference_vector', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), + ('inputs', 'expected'), CASES_CALC_PTF_FERRERJULIA2004_HUMIC_ACRISOL_TEXTURE_ORGANIC_MATTER, + ids=CASES_CALC_PTF_FERRERJULIA2004_HUMIC_ACRISOL_TEXTURE_ORGANIC_MATTER_IDS, ) -def test_calc_ptf_ferrerjulia2004_humic_acrisol_texture_organic_matter_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_ferrerjulia2004_humic_acrisol_texture_organic_matter_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_ferrerjulia2004_humic_acrisol_texture_organic_matter(**inputs) - assert result == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result, + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_humic_acrisol_texture_organic_matter_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_FERRERJULIA2004_HUMIC_ACRISOL_TEXTURE_ORGANIC_MATTER ) result = calc_ptf_ferrerjulia2004_humic_acrisol_texture_organic_matter(**inputs, out=None) - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_humic_acrisol_texture_organic_matter_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_FERRERJULIA2004_HUMIC_ACRISOL_TEXTURE_ORGANIC_MATTER ) result = calc_ptf_ferrerjulia2004_humic_acrisol_texture_organic_matter(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) CASES_CALC_PTF_FERRERJULIA2004_CALCIC_CAMBISOL_SAND = [ - ({'sand': 50.0}, {'k_sat': 9.995162000935244}, 1e-12, 1e-12), + ({'sand': 50.0}, {'k_sat': 9.995162000935244}), +] +CASES_CALC_PTF_FERRERJULIA2004_CALCIC_CAMBISOL_SAND_IDS = [ + 'reviewer_reference_vector', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_FERRERJULIA2004_CALCIC_CAMBISOL_SAND + ('inputs', 'expected'), + CASES_CALC_PTF_FERRERJULIA2004_CALCIC_CAMBISOL_SAND, + ids=CASES_CALC_PTF_FERRERJULIA2004_CALCIC_CAMBISOL_SAND_IDS, ) -def test_calc_ptf_ferrerjulia2004_calcic_cambisol_sand_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_ferrerjulia2004_calcic_cambisol_sand_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_ferrerjulia2004_calcic_cambisol_sand(**inputs) - assert result == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result, + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_calcic_cambisol_sand_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_FERRERJULIA2004_CALCIC_CAMBISOL_SAND ) result = calc_ptf_ferrerjulia2004_calcic_cambisol_sand(**inputs, out=None) - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_calcic_cambisol_sand_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( - CASES_CALC_PTF_FERRERJULIA2004_CALCIC_CAMBISOL_SAND - ) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_FERRERJULIA2004_CALCIC_CAMBISOL_SAND) result = calc_ptf_ferrerjulia2004_calcic_cambisol_sand(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) CASES_CALC_PTF_FERRERJULIA2004_CALCIC_CAMBISOL_TEXTURE_ORGANIC_MATTER = [ - ({'clay': 25.0, 'organic_matter': 2.5, 'sand': 50.0}, {'k_sat': 16.51625}, 1e-12, 1e-12), + ({'clay': 25.0, 'organic_matter': 2.5, 'sand': 50.0}, {'k_sat': 16.51625}), +] +CASES_CALC_PTF_FERRERJULIA2004_CALCIC_CAMBISOL_TEXTURE_ORGANIC_MATTER_IDS = [ + 'reviewer_reference_vector', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), + ('inputs', 'expected'), CASES_CALC_PTF_FERRERJULIA2004_CALCIC_CAMBISOL_TEXTURE_ORGANIC_MATTER, + ids=CASES_CALC_PTF_FERRERJULIA2004_CALCIC_CAMBISOL_TEXTURE_ORGANIC_MATTER_IDS, ) -def test_calc_ptf_ferrerjulia2004_calcic_cambisol_texture_organic_matter_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_ferrerjulia2004_calcic_cambisol_texture_organic_matter_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_ferrerjulia2004_calcic_cambisol_texture_organic_matter(**inputs) - assert result == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result, + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_calcic_cambisol_texture_organic_matter_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_FERRERJULIA2004_CALCIC_CAMBISOL_TEXTURE_ORGANIC_MATTER ) result = calc_ptf_ferrerjulia2004_calcic_cambisol_texture_organic_matter(**inputs, out=None) - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_calcic_cambisol_texture_organic_matter_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_FERRERJULIA2004_CALCIC_CAMBISOL_TEXTURE_ORGANIC_MATTER ) result = calc_ptf_ferrerjulia2004_calcic_cambisol_texture_organic_matter(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) CASES_CALC_PTF_FERRERJULIA2004_DYSTRIC_CAMBISOL_SAND = [ - ({'sand': 50.0}, {'k_sat': 13.413573870265868}, 1e-12, 1e-12), + ({'sand': 50.0}, {'k_sat': 13.413573870265868}), +] +CASES_CALC_PTF_FERRERJULIA2004_DYSTRIC_CAMBISOL_SAND_IDS = [ + 'reviewer_reference_vector', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_FERRERJULIA2004_DYSTRIC_CAMBISOL_SAND + ('inputs', 'expected'), + CASES_CALC_PTF_FERRERJULIA2004_DYSTRIC_CAMBISOL_SAND, + ids=CASES_CALC_PTF_FERRERJULIA2004_DYSTRIC_CAMBISOL_SAND_IDS, ) -def test_calc_ptf_ferrerjulia2004_dystric_cambisol_sand_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_ferrerjulia2004_dystric_cambisol_sand_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_ferrerjulia2004_dystric_cambisol_sand(**inputs) - assert result == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result, + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_dystric_cambisol_sand_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_FERRERJULIA2004_DYSTRIC_CAMBISOL_SAND ) result = calc_ptf_ferrerjulia2004_dystric_cambisol_sand(**inputs, out=None) - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_dystric_cambisol_sand_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_FERRERJULIA2004_DYSTRIC_CAMBISOL_SAND ) result = calc_ptf_ferrerjulia2004_dystric_cambisol_sand(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) CASES_CALC_PTF_FERRERJULIA2004_DYSTRIC_CAMBISOL_TEXTURE_ORGANIC_MATTER = [ - ({'clay': 25.0, 'organic_matter': 2.5, 'sand': 50.0}, {'k_sat': 20.79}, 1e-12, 1e-12), + ({'clay': 25.0, 'organic_matter': 2.5, 'sand': 50.0}, {'k_sat': 20.79}), +] +CASES_CALC_PTF_FERRERJULIA2004_DYSTRIC_CAMBISOL_TEXTURE_ORGANIC_MATTER_IDS = [ + 'reviewer_reference_vector', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), + ('inputs', 'expected'), CASES_CALC_PTF_FERRERJULIA2004_DYSTRIC_CAMBISOL_TEXTURE_ORGANIC_MATTER, + ids=CASES_CALC_PTF_FERRERJULIA2004_DYSTRIC_CAMBISOL_TEXTURE_ORGANIC_MATTER_IDS, ) -def test_calc_ptf_ferrerjulia2004_dystric_cambisol_texture_organic_matter_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_ferrerjulia2004_dystric_cambisol_texture_organic_matter_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_ferrerjulia2004_dystric_cambisol_texture_organic_matter(**inputs) - assert result == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result, + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_dystric_cambisol_texture_organic_matter_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_FERRERJULIA2004_DYSTRIC_CAMBISOL_TEXTURE_ORGANIC_MATTER ) result = calc_ptf_ferrerjulia2004_dystric_cambisol_texture_organic_matter(**inputs, out=None) - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_dystric_cambisol_texture_organic_matter_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_FERRERJULIA2004_DYSTRIC_CAMBISOL_TEXTURE_ORGANIC_MATTER ) result = calc_ptf_ferrerjulia2004_dystric_cambisol_texture_organic_matter(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) CASES_CALC_PTF_FERRERJULIA2004_EUTRIC_CAMBISOL_SAND = [ - ({'sand': 50.0}, {'k_sat': 11.570199173737363}, 1e-12, 1e-12), + ({'sand': 50.0}, {'k_sat': 11.570199173737363}), +] +CASES_CALC_PTF_FERRERJULIA2004_EUTRIC_CAMBISOL_SAND_IDS = [ + 'reviewer_reference_vector', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_FERRERJULIA2004_EUTRIC_CAMBISOL_SAND + ('inputs', 'expected'), + CASES_CALC_PTF_FERRERJULIA2004_EUTRIC_CAMBISOL_SAND, + ids=CASES_CALC_PTF_FERRERJULIA2004_EUTRIC_CAMBISOL_SAND_IDS, ) -def test_calc_ptf_ferrerjulia2004_eutric_cambisol_sand_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_ferrerjulia2004_eutric_cambisol_sand_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_ferrerjulia2004_eutric_cambisol_sand(**inputs) - assert result == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result, + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_eutric_cambisol_sand_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_FERRERJULIA2004_EUTRIC_CAMBISOL_SAND ) result = calc_ptf_ferrerjulia2004_eutric_cambisol_sand(**inputs, out=None) - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_eutric_cambisol_sand_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( - CASES_CALC_PTF_FERRERJULIA2004_EUTRIC_CAMBISOL_SAND - ) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_FERRERJULIA2004_EUTRIC_CAMBISOL_SAND) result = calc_ptf_ferrerjulia2004_eutric_cambisol_sand(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) CASES_CALC_PTF_FERRERJULIA2004_EUTRIC_CAMBISOL_TEXTURE_ORGANIC_MATTER = [ - ({'clay': 25.0, 'organic_matter': 2.5, 'sand': 50.0}, {'k_sat': 19.5235}, 1e-12, 1e-12), + ({'clay': 25.0, 'organic_matter': 2.5, 'sand': 50.0}, {'k_sat': 19.5235}), +] +CASES_CALC_PTF_FERRERJULIA2004_EUTRIC_CAMBISOL_TEXTURE_ORGANIC_MATTER_IDS = [ + 'reviewer_reference_vector', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), + ('inputs', 'expected'), CASES_CALC_PTF_FERRERJULIA2004_EUTRIC_CAMBISOL_TEXTURE_ORGANIC_MATTER, + ids=CASES_CALC_PTF_FERRERJULIA2004_EUTRIC_CAMBISOL_TEXTURE_ORGANIC_MATTER_IDS, ) -def test_calc_ptf_ferrerjulia2004_eutric_cambisol_texture_organic_matter_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_ferrerjulia2004_eutric_cambisol_texture_organic_matter_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_ferrerjulia2004_eutric_cambisol_texture_organic_matter(**inputs) - assert result == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result, + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_eutric_cambisol_texture_organic_matter_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_FERRERJULIA2004_EUTRIC_CAMBISOL_TEXTURE_ORGANIC_MATTER ) result = calc_ptf_ferrerjulia2004_eutric_cambisol_texture_organic_matter(**inputs, out=None) - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_eutric_cambisol_texture_organic_matter_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_FERRERJULIA2004_EUTRIC_CAMBISOL_TEXTURE_ORGANIC_MATTER ) result = calc_ptf_ferrerjulia2004_eutric_cambisol_texture_organic_matter(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) CASES_CALC_PTF_FERRERJULIA2004_GLEYIC_CAMBISOL_SAND = [ - ({'sand': 50.0}, {'k_sat': 6.559110321079926}, 1e-12, 1e-12), + ({'sand': 50.0}, {'k_sat': 6.559110321079926}), +] +CASES_CALC_PTF_FERRERJULIA2004_GLEYIC_CAMBISOL_SAND_IDS = [ + 'reviewer_reference_vector', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_FERRERJULIA2004_GLEYIC_CAMBISOL_SAND + ('inputs', 'expected'), + CASES_CALC_PTF_FERRERJULIA2004_GLEYIC_CAMBISOL_SAND, + ids=CASES_CALC_PTF_FERRERJULIA2004_GLEYIC_CAMBISOL_SAND_IDS, ) -def test_calc_ptf_ferrerjulia2004_gleyic_cambisol_sand_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_ferrerjulia2004_gleyic_cambisol_sand_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_ferrerjulia2004_gleyic_cambisol_sand(**inputs) - assert result == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result, + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_gleyic_cambisol_sand_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_FERRERJULIA2004_GLEYIC_CAMBISOL_SAND ) result = calc_ptf_ferrerjulia2004_gleyic_cambisol_sand(**inputs, out=None) - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_gleyic_cambisol_sand_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( - CASES_CALC_PTF_FERRERJULIA2004_GLEYIC_CAMBISOL_SAND - ) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_FERRERJULIA2004_GLEYIC_CAMBISOL_SAND) result = calc_ptf_ferrerjulia2004_gleyic_cambisol_sand(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) CASES_CALC_PTF_FERRERJULIA2004_GLEYIC_CAMBISOL_TEXTURE_ORGANIC_MATTER = [ - ({'clay': 25.0, 'organic_matter': 2.5, 'sand': 50.0}, {'k_sat': 11.514}, 1e-12, 1e-12), + ({'clay': 25.0, 'organic_matter': 2.5, 'sand': 50.0}, {'k_sat': 11.514}), +] +CASES_CALC_PTF_FERRERJULIA2004_GLEYIC_CAMBISOL_TEXTURE_ORGANIC_MATTER_IDS = [ + 'reviewer_reference_vector', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), + ('inputs', 'expected'), CASES_CALC_PTF_FERRERJULIA2004_GLEYIC_CAMBISOL_TEXTURE_ORGANIC_MATTER, + ids=CASES_CALC_PTF_FERRERJULIA2004_GLEYIC_CAMBISOL_TEXTURE_ORGANIC_MATTER_IDS, ) -def test_calc_ptf_ferrerjulia2004_gleyic_cambisol_texture_organic_matter_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_ferrerjulia2004_gleyic_cambisol_texture_organic_matter_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_ferrerjulia2004_gleyic_cambisol_texture_organic_matter(**inputs) - assert result == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result, + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_gleyic_cambisol_texture_organic_matter_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_FERRERJULIA2004_GLEYIC_CAMBISOL_TEXTURE_ORGANIC_MATTER ) result = calc_ptf_ferrerjulia2004_gleyic_cambisol_texture_organic_matter(**inputs, out=None) - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_gleyic_cambisol_texture_organic_matter_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_FERRERJULIA2004_GLEYIC_CAMBISOL_TEXTURE_ORGANIC_MATTER ) result = calc_ptf_ferrerjulia2004_gleyic_cambisol_texture_organic_matter(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) CASES_CALC_PTF_FERRERJULIA2004_HUMIC_CAMBISOL_SAND = [ - ({'sand': 50.0}, {'k_sat': 13.820129957492508}, 1e-12, 1e-12), + ({'sand': 50.0}, {'k_sat': 13.820129957492508}), +] +CASES_CALC_PTF_FERRERJULIA2004_HUMIC_CAMBISOL_SAND_IDS = [ + 'reviewer_reference_vector', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_FERRERJULIA2004_HUMIC_CAMBISOL_SAND + ('inputs', 'expected'), + CASES_CALC_PTF_FERRERJULIA2004_HUMIC_CAMBISOL_SAND, + ids=CASES_CALC_PTF_FERRERJULIA2004_HUMIC_CAMBISOL_SAND_IDS, ) -def test_calc_ptf_ferrerjulia2004_humic_cambisol_sand_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_ferrerjulia2004_humic_cambisol_sand_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_ferrerjulia2004_humic_cambisol_sand(**inputs) - assert result == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result, + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_humic_cambisol_sand_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( - CASES_CALC_PTF_FERRERJULIA2004_HUMIC_CAMBISOL_SAND - ) + inputs, expected, _out = prepare_vector_case(CASES_CALC_PTF_FERRERJULIA2004_HUMIC_CAMBISOL_SAND) result = calc_ptf_ferrerjulia2004_humic_cambisol_sand(**inputs, out=None) - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_humic_cambisol_sand_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( - CASES_CALC_PTF_FERRERJULIA2004_HUMIC_CAMBISOL_SAND - ) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_FERRERJULIA2004_HUMIC_CAMBISOL_SAND) result = calc_ptf_ferrerjulia2004_humic_cambisol_sand(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) CASES_CALC_PTF_FERRERJULIA2004_HUMIC_CAMBISOL_TEXTURE_ORGANIC_MATTER = [ - ({'clay': 25.0, 'organic_matter': 2.5, 'sand': 50.0}, {'k_sat': 20.9029}, 1e-12, 1e-12), + ({'clay': 25.0, 'organic_matter': 2.5, 'sand': 50.0}, {'k_sat': 20.9029}), +] +CASES_CALC_PTF_FERRERJULIA2004_HUMIC_CAMBISOL_TEXTURE_ORGANIC_MATTER_IDS = [ + 'reviewer_reference_vector', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), + ('inputs', 'expected'), CASES_CALC_PTF_FERRERJULIA2004_HUMIC_CAMBISOL_TEXTURE_ORGANIC_MATTER, + ids=CASES_CALC_PTF_FERRERJULIA2004_HUMIC_CAMBISOL_TEXTURE_ORGANIC_MATTER_IDS, ) -def test_calc_ptf_ferrerjulia2004_humic_cambisol_texture_organic_matter_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_ferrerjulia2004_humic_cambisol_texture_organic_matter_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_ferrerjulia2004_humic_cambisol_texture_organic_matter(**inputs) - assert result == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result, + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_humic_cambisol_texture_organic_matter_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_FERRERJULIA2004_HUMIC_CAMBISOL_TEXTURE_ORGANIC_MATTER ) result = calc_ptf_ferrerjulia2004_humic_cambisol_texture_organic_matter(**inputs, out=None) - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_humic_cambisol_texture_organic_matter_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_FERRERJULIA2004_HUMIC_CAMBISOL_TEXTURE_ORGANIC_MATTER ) result = calc_ptf_ferrerjulia2004_humic_cambisol_texture_organic_matter(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) CASES_CALC_PTF_FERRERJULIA2004_CALCARIC_FLUVISOL_SAND = [ - ({'sand': 50.0}, {'k_sat': 10.959482760491376}, 1e-12, 1e-12), + ({'sand': 50.0}, {'k_sat': 10.959482760491376}), +] +CASES_CALC_PTF_FERRERJULIA2004_CALCARIC_FLUVISOL_SAND_IDS = [ + 'reviewer_reference_vector', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_FERRERJULIA2004_CALCARIC_FLUVISOL_SAND + ('inputs', 'expected'), + CASES_CALC_PTF_FERRERJULIA2004_CALCARIC_FLUVISOL_SAND, + ids=CASES_CALC_PTF_FERRERJULIA2004_CALCARIC_FLUVISOL_SAND_IDS, ) -def test_calc_ptf_ferrerjulia2004_calcaric_fluvisol_sand_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_ferrerjulia2004_calcaric_fluvisol_sand_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_ferrerjulia2004_calcaric_fluvisol_sand(**inputs) - assert result == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result, + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_calcaric_fluvisol_sand_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_FERRERJULIA2004_CALCARIC_FLUVISOL_SAND ) result = calc_ptf_ferrerjulia2004_calcaric_fluvisol_sand(**inputs, out=None) - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_calcaric_fluvisol_sand_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_FERRERJULIA2004_CALCARIC_FLUVISOL_SAND ) result = calc_ptf_ferrerjulia2004_calcaric_fluvisol_sand(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) CASES_CALC_PTF_FERRERJULIA2004_CALCARIC_FLUVISOL_TEXTURE_ORGANIC_MATTER = [ - ({'clay': 25.0, 'organic_matter': 2.5, 'sand': 50.0}, {'k_sat': 13.101}, 1e-12, 1e-12), + ({'clay': 25.0, 'organic_matter': 2.5, 'sand': 50.0}, {'k_sat': 13.101}), +] +CASES_CALC_PTF_FERRERJULIA2004_CALCARIC_FLUVISOL_TEXTURE_ORGANIC_MATTER_IDS = [ + 'reviewer_reference_vector', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), + ('inputs', 'expected'), CASES_CALC_PTF_FERRERJULIA2004_CALCARIC_FLUVISOL_TEXTURE_ORGANIC_MATTER, + ids=CASES_CALC_PTF_FERRERJULIA2004_CALCARIC_FLUVISOL_TEXTURE_ORGANIC_MATTER_IDS, ) -def test_calc_ptf_ferrerjulia2004_calcaric_fluvisol_texture_organic_matter_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_ferrerjulia2004_calcaric_fluvisol_texture_organic_matter_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_ferrerjulia2004_calcaric_fluvisol_texture_organic_matter(**inputs) - assert result == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result, + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_calcaric_fluvisol_texture_organic_matter_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_FERRERJULIA2004_CALCARIC_FLUVISOL_TEXTURE_ORGANIC_MATTER ) result = calc_ptf_ferrerjulia2004_calcaric_fluvisol_texture_organic_matter(**inputs, out=None) - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_calcaric_fluvisol_texture_organic_matter_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_FERRERJULIA2004_CALCARIC_FLUVISOL_TEXTURE_ORGANIC_MATTER ) result = calc_ptf_ferrerjulia2004_calcaric_fluvisol_texture_organic_matter(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) CASES_CALC_PTF_FERRERJULIA2004_CALCIC_LUVISOL_SAND = [ - ({'sand': 50.0}, {'k_sat': 6.931067612606957}, 1e-12, 1e-12), + ({'sand': 50.0}, {'k_sat': 6.931067612606957}), +] +CASES_CALC_PTF_FERRERJULIA2004_CALCIC_LUVISOL_SAND_IDS = [ + 'reviewer_reference_vector', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_FERRERJULIA2004_CALCIC_LUVISOL_SAND + ('inputs', 'expected'), + CASES_CALC_PTF_FERRERJULIA2004_CALCIC_LUVISOL_SAND, + ids=CASES_CALC_PTF_FERRERJULIA2004_CALCIC_LUVISOL_SAND_IDS, ) -def test_calc_ptf_ferrerjulia2004_calcic_luvisol_sand_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_ferrerjulia2004_calcic_luvisol_sand_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_ferrerjulia2004_calcic_luvisol_sand(**inputs) - assert result == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result, + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_calcic_luvisol_sand_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( - CASES_CALC_PTF_FERRERJULIA2004_CALCIC_LUVISOL_SAND - ) + inputs, expected, _out = prepare_vector_case(CASES_CALC_PTF_FERRERJULIA2004_CALCIC_LUVISOL_SAND) result = calc_ptf_ferrerjulia2004_calcic_luvisol_sand(**inputs, out=None) - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_calcic_luvisol_sand_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( - CASES_CALC_PTF_FERRERJULIA2004_CALCIC_LUVISOL_SAND - ) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_FERRERJULIA2004_CALCIC_LUVISOL_SAND) result = calc_ptf_ferrerjulia2004_calcic_luvisol_sand(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) CASES_CALC_PTF_FERRERJULIA2004_CALCIC_LUVISOL_TEXTURE_ORGANIC_MATTER = [ - ({'clay': 25.0, 'organic_matter': 2.5, 'sand': 50.0}, {'k_sat': 8.9332}, 1e-12, 1e-12), + ({'clay': 25.0, 'organic_matter': 2.5, 'sand': 50.0}, {'k_sat': 8.9332}), +] +CASES_CALC_PTF_FERRERJULIA2004_CALCIC_LUVISOL_TEXTURE_ORGANIC_MATTER_IDS = [ + 'reviewer_reference_vector', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), + ('inputs', 'expected'), CASES_CALC_PTF_FERRERJULIA2004_CALCIC_LUVISOL_TEXTURE_ORGANIC_MATTER, + ids=CASES_CALC_PTF_FERRERJULIA2004_CALCIC_LUVISOL_TEXTURE_ORGANIC_MATTER_IDS, ) -def test_calc_ptf_ferrerjulia2004_calcic_luvisol_texture_organic_matter_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_ferrerjulia2004_calcic_luvisol_texture_organic_matter_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_ferrerjulia2004_calcic_luvisol_texture_organic_matter(**inputs) - assert result == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result, + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_calcic_luvisol_texture_organic_matter_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_FERRERJULIA2004_CALCIC_LUVISOL_TEXTURE_ORGANIC_MATTER ) result = calc_ptf_ferrerjulia2004_calcic_luvisol_texture_organic_matter(**inputs, out=None) - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_calcic_luvisol_texture_organic_matter_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_FERRERJULIA2004_CALCIC_LUVISOL_TEXTURE_ORGANIC_MATTER ) result = calc_ptf_ferrerjulia2004_calcic_luvisol_texture_organic_matter(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) CASES_CALC_PTF_FERRERJULIA2004_CHROMIC_LUVISOL_SAND = [ - ({'sand': 50.0}, {'k_sat': 10.61655727277843}, 1e-12, 1e-12), + ({'sand': 50.0}, {'k_sat': 10.61655727277843}), +] +CASES_CALC_PTF_FERRERJULIA2004_CHROMIC_LUVISOL_SAND_IDS = [ + 'reviewer_reference_vector', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_FERRERJULIA2004_CHROMIC_LUVISOL_SAND + ('inputs', 'expected'), + CASES_CALC_PTF_FERRERJULIA2004_CHROMIC_LUVISOL_SAND, + ids=CASES_CALC_PTF_FERRERJULIA2004_CHROMIC_LUVISOL_SAND_IDS, ) -def test_calc_ptf_ferrerjulia2004_chromic_luvisol_sand_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_ferrerjulia2004_chromic_luvisol_sand_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_ferrerjulia2004_chromic_luvisol_sand(**inputs) - assert result == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result, + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_chromic_luvisol_sand_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_FERRERJULIA2004_CHROMIC_LUVISOL_SAND ) result = calc_ptf_ferrerjulia2004_chromic_luvisol_sand(**inputs, out=None) - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_chromic_luvisol_sand_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( - CASES_CALC_PTF_FERRERJULIA2004_CHROMIC_LUVISOL_SAND - ) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_FERRERJULIA2004_CHROMIC_LUVISOL_SAND) result = calc_ptf_ferrerjulia2004_chromic_luvisol_sand(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) CASES_CALC_PTF_FERRERJULIA2004_CHROMIC_LUVISOL_TEXTURE_ORGANIC_MATTER = [ - ({'clay': 25.0, 'organic_matter': 2.5, 'sand': 50.0}, {'k_sat': 17.1465}, 1e-12, 1e-12), + ({'clay': 25.0, 'organic_matter': 2.5, 'sand': 50.0}, {'k_sat': 17.1465}), +] +CASES_CALC_PTF_FERRERJULIA2004_CHROMIC_LUVISOL_TEXTURE_ORGANIC_MATTER_IDS = [ + 'reviewer_reference_vector', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), + ('inputs', 'expected'), CASES_CALC_PTF_FERRERJULIA2004_CHROMIC_LUVISOL_TEXTURE_ORGANIC_MATTER, + ids=CASES_CALC_PTF_FERRERJULIA2004_CHROMIC_LUVISOL_TEXTURE_ORGANIC_MATTER_IDS, ) -def test_calc_ptf_ferrerjulia2004_chromic_luvisol_texture_organic_matter_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_ferrerjulia2004_chromic_luvisol_texture_organic_matter_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_ferrerjulia2004_chromic_luvisol_texture_organic_matter(**inputs) - assert result == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result, + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_chromic_luvisol_texture_organic_matter_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_FERRERJULIA2004_CHROMIC_LUVISOL_TEXTURE_ORGANIC_MATTER ) result = calc_ptf_ferrerjulia2004_chromic_luvisol_texture_organic_matter(**inputs, out=None) - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_chromic_luvisol_texture_organic_matter_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_FERRERJULIA2004_CHROMIC_LUVISOL_TEXTURE_ORGANIC_MATTER ) result = calc_ptf_ferrerjulia2004_chromic_luvisol_texture_organic_matter(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) CASES_CALC_PTF_FERRERJULIA2004_GLEYIC_LUVISOL_SAND = [ - ({'sand': 50.0}, {'k_sat': 7.049012285884197}, 1e-12, 1e-12), + ({'sand': 50.0}, {'k_sat': 7.049012285884197}), +] +CASES_CALC_PTF_FERRERJULIA2004_GLEYIC_LUVISOL_SAND_IDS = [ + 'reviewer_reference_vector', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_FERRERJULIA2004_GLEYIC_LUVISOL_SAND + ('inputs', 'expected'), + CASES_CALC_PTF_FERRERJULIA2004_GLEYIC_LUVISOL_SAND, + ids=CASES_CALC_PTF_FERRERJULIA2004_GLEYIC_LUVISOL_SAND_IDS, ) -def test_calc_ptf_ferrerjulia2004_gleyic_luvisol_sand_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_ferrerjulia2004_gleyic_luvisol_sand_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_ferrerjulia2004_gleyic_luvisol_sand(**inputs) - assert result == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result, + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_gleyic_luvisol_sand_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( - CASES_CALC_PTF_FERRERJULIA2004_GLEYIC_LUVISOL_SAND - ) + inputs, expected, _out = prepare_vector_case(CASES_CALC_PTF_FERRERJULIA2004_GLEYIC_LUVISOL_SAND) result = calc_ptf_ferrerjulia2004_gleyic_luvisol_sand(**inputs, out=None) - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_gleyic_luvisol_sand_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( - CASES_CALC_PTF_FERRERJULIA2004_GLEYIC_LUVISOL_SAND - ) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_FERRERJULIA2004_GLEYIC_LUVISOL_SAND) result = calc_ptf_ferrerjulia2004_gleyic_luvisol_sand(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) CASES_CALC_PTF_FERRERJULIA2004_GLEYIC_LUVISOL_TEXTURE_ORGANIC_MATTER = [ - ({'clay': 25.0, 'organic_matter': 2.5, 'sand': 50.0}, {'k_sat': 19.55}, 1e-12, 1e-12), + ({'clay': 25.0, 'organic_matter': 2.5, 'sand': 50.0}, {'k_sat': 19.55}), +] +CASES_CALC_PTF_FERRERJULIA2004_GLEYIC_LUVISOL_TEXTURE_ORGANIC_MATTER_IDS = [ + 'reviewer_reference_vector', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), + ('inputs', 'expected'), CASES_CALC_PTF_FERRERJULIA2004_GLEYIC_LUVISOL_TEXTURE_ORGANIC_MATTER, + ids=CASES_CALC_PTF_FERRERJULIA2004_GLEYIC_LUVISOL_TEXTURE_ORGANIC_MATTER_IDS, ) -def test_calc_ptf_ferrerjulia2004_gleyic_luvisol_texture_organic_matter_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_ferrerjulia2004_gleyic_luvisol_texture_organic_matter_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_ferrerjulia2004_gleyic_luvisol_texture_organic_matter(**inputs) - assert result == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result, + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_gleyic_luvisol_texture_organic_matter_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_FERRERJULIA2004_GLEYIC_LUVISOL_TEXTURE_ORGANIC_MATTER ) result = calc_ptf_ferrerjulia2004_gleyic_luvisol_texture_organic_matter(**inputs, out=None) - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_gleyic_luvisol_texture_organic_matter_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_FERRERJULIA2004_GLEYIC_LUVISOL_TEXTURE_ORGANIC_MATTER ) result = calc_ptf_ferrerjulia2004_gleyic_luvisol_texture_organic_matter(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) CASES_CALC_PTF_FERRERJULIA2004_ORTHIC_LUVISOL_SAND = [ - ({'sand': 50.0}, {'k_sat': 12.04280911180458}, 1e-12, 1e-12), + ({'sand': 50.0}, {'k_sat': 12.04280911180458}), +] +CASES_CALC_PTF_FERRERJULIA2004_ORTHIC_LUVISOL_SAND_IDS = [ + 'reviewer_reference_vector', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_FERRERJULIA2004_ORTHIC_LUVISOL_SAND + ('inputs', 'expected'), + CASES_CALC_PTF_FERRERJULIA2004_ORTHIC_LUVISOL_SAND, + ids=CASES_CALC_PTF_FERRERJULIA2004_ORTHIC_LUVISOL_SAND_IDS, ) -def test_calc_ptf_ferrerjulia2004_orthic_luvisol_sand_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_ferrerjulia2004_orthic_luvisol_sand_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_ferrerjulia2004_orthic_luvisol_sand(**inputs) - assert result == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result, + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_orthic_luvisol_sand_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( - CASES_CALC_PTF_FERRERJULIA2004_ORTHIC_LUVISOL_SAND - ) + inputs, expected, _out = prepare_vector_case(CASES_CALC_PTF_FERRERJULIA2004_ORTHIC_LUVISOL_SAND) result = calc_ptf_ferrerjulia2004_orthic_luvisol_sand(**inputs, out=None) - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_orthic_luvisol_sand_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( - CASES_CALC_PTF_FERRERJULIA2004_ORTHIC_LUVISOL_SAND - ) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_FERRERJULIA2004_ORTHIC_LUVISOL_SAND) result = calc_ptf_ferrerjulia2004_orthic_luvisol_sand(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) CASES_CALC_PTF_FERRERJULIA2004_ORTHIC_LUVISOL_TEXTURE_ORGANIC_MATTER = [ - ({'clay': 25.0, 'organic_matter': 2.5, 'sand': 50.0}, {'k_sat': 21.40425}, 1e-12, 1e-12), + ({'clay': 25.0, 'organic_matter': 2.5, 'sand': 50.0}, {'k_sat': 21.40425}), +] +CASES_CALC_PTF_FERRERJULIA2004_ORTHIC_LUVISOL_TEXTURE_ORGANIC_MATTER_IDS = [ + 'reviewer_reference_vector', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), + ('inputs', 'expected'), CASES_CALC_PTF_FERRERJULIA2004_ORTHIC_LUVISOL_TEXTURE_ORGANIC_MATTER, + ids=CASES_CALC_PTF_FERRERJULIA2004_ORTHIC_LUVISOL_TEXTURE_ORGANIC_MATTER_IDS, ) -def test_calc_ptf_ferrerjulia2004_orthic_luvisol_texture_organic_matter_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_ferrerjulia2004_orthic_luvisol_texture_organic_matter_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_ferrerjulia2004_orthic_luvisol_texture_organic_matter(**inputs) - assert result == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result, + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_orthic_luvisol_texture_organic_matter_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_FERRERJULIA2004_ORTHIC_LUVISOL_TEXTURE_ORGANIC_MATTER ) result = calc_ptf_ferrerjulia2004_orthic_luvisol_texture_organic_matter(**inputs, out=None) - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_orthic_luvisol_texture_organic_matter_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_FERRERJULIA2004_ORTHIC_LUVISOL_TEXTURE_ORGANIC_MATTER ) result = calc_ptf_ferrerjulia2004_orthic_luvisol_texture_organic_matter(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) CASES_CALC_PTF_FERRERJULIA2004_RANKER_SAND = [ - ({'sand': 50.0}, {'k_sat': 12.423399456061938}, 1e-12, 1e-12), + ({'sand': 50.0}, {'k_sat': 12.423399456061938}), +] +CASES_CALC_PTF_FERRERJULIA2004_RANKER_SAND_IDS = [ + 'reviewer_reference_vector', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_FERRERJULIA2004_RANKER_SAND + ('inputs', 'expected'), + CASES_CALC_PTF_FERRERJULIA2004_RANKER_SAND, + ids=CASES_CALC_PTF_FERRERJULIA2004_RANKER_SAND_IDS, ) -def test_calc_ptf_ferrerjulia2004_ranker_sand_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_ferrerjulia2004_ranker_sand_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_ferrerjulia2004_ranker_sand(**inputs) - assert result == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result, + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_ranker_sand_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( - CASES_CALC_PTF_FERRERJULIA2004_RANKER_SAND - ) + inputs, expected, _out = prepare_vector_case(CASES_CALC_PTF_FERRERJULIA2004_RANKER_SAND) result = calc_ptf_ferrerjulia2004_ranker_sand(**inputs, out=None) - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_ranker_sand_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( - CASES_CALC_PTF_FERRERJULIA2004_RANKER_SAND - ) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_FERRERJULIA2004_RANKER_SAND) result = calc_ptf_ferrerjulia2004_ranker_sand(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) CASES_CALC_PTF_FERRERJULIA2004_RANKER_TEXTURE_ORGANIC_MATTER = [ - ({'clay': 25.0, 'organic_matter': 2.5, 'sand': 50.0}, {'k_sat': 22.4515}, 1e-12, 1e-12), + ({'clay': 25.0, 'organic_matter': 2.5, 'sand': 50.0}, {'k_sat': 22.4515}), +] +CASES_CALC_PTF_FERRERJULIA2004_RANKER_TEXTURE_ORGANIC_MATTER_IDS = [ + 'reviewer_reference_vector', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), + ('inputs', 'expected'), CASES_CALC_PTF_FERRERJULIA2004_RANKER_TEXTURE_ORGANIC_MATTER, + ids=CASES_CALC_PTF_FERRERJULIA2004_RANKER_TEXTURE_ORGANIC_MATTER_IDS, ) -def test_calc_ptf_ferrerjulia2004_ranker_texture_organic_matter_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_ferrerjulia2004_ranker_texture_organic_matter_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_ferrerjulia2004_ranker_texture_organic_matter(**inputs) - assert result == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result, + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_ranker_texture_organic_matter_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_FERRERJULIA2004_RANKER_TEXTURE_ORGANIC_MATTER ) result = calc_ptf_ferrerjulia2004_ranker_texture_organic_matter(**inputs, out=None) - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_ranker_texture_organic_matter_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_FERRERJULIA2004_RANKER_TEXTURE_ORGANIC_MATTER ) result = calc_ptf_ferrerjulia2004_ranker_texture_organic_matter(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) CASES_CALC_PTF_FERRERJULIA2004_CALCARIC_REGOSOL_SAND = [ - ({'sand': 50.0}, {'k_sat': 12.267374733833096}, 1e-12, 1e-12), + ({'sand': 50.0}, {'k_sat': 12.267374733833096}), +] +CASES_CALC_PTF_FERRERJULIA2004_CALCARIC_REGOSOL_SAND_IDS = [ + 'reviewer_reference_vector', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_FERRERJULIA2004_CALCARIC_REGOSOL_SAND + ('inputs', 'expected'), + CASES_CALC_PTF_FERRERJULIA2004_CALCARIC_REGOSOL_SAND, + ids=CASES_CALC_PTF_FERRERJULIA2004_CALCARIC_REGOSOL_SAND_IDS, ) -def test_calc_ptf_ferrerjulia2004_calcaric_regosol_sand_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_ferrerjulia2004_calcaric_regosol_sand_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_ferrerjulia2004_calcaric_regosol_sand(**inputs) - assert result == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result, + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_calcaric_regosol_sand_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_FERRERJULIA2004_CALCARIC_REGOSOL_SAND ) result = calc_ptf_ferrerjulia2004_calcaric_regosol_sand(**inputs, out=None) - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_calcaric_regosol_sand_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_FERRERJULIA2004_CALCARIC_REGOSOL_SAND ) result = calc_ptf_ferrerjulia2004_calcaric_regosol_sand(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) CASES_CALC_PTF_FERRERJULIA2004_CALCARIC_REGOSOL_TEXTURE_ORGANIC_MATTER = [ - ({'clay': 25.0, 'organic_matter': 2.5, 'sand': 50.0}, {'k_sat': 20.532}, 1e-12, 1e-12), + ({'clay': 25.0, 'organic_matter': 2.5, 'sand': 50.0}, {'k_sat': 20.532}), +] +CASES_CALC_PTF_FERRERJULIA2004_CALCARIC_REGOSOL_TEXTURE_ORGANIC_MATTER_IDS = [ + 'reviewer_reference_vector', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), + ('inputs', 'expected'), CASES_CALC_PTF_FERRERJULIA2004_CALCARIC_REGOSOL_TEXTURE_ORGANIC_MATTER, + ids=CASES_CALC_PTF_FERRERJULIA2004_CALCARIC_REGOSOL_TEXTURE_ORGANIC_MATTER_IDS, ) -def test_calc_ptf_ferrerjulia2004_calcaric_regosol_texture_organic_matter_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_ferrerjulia2004_calcaric_regosol_texture_organic_matter_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_ferrerjulia2004_calcaric_regosol_texture_organic_matter(**inputs) - assert result == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result, + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_calcaric_regosol_texture_organic_matter_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_FERRERJULIA2004_CALCARIC_REGOSOL_TEXTURE_ORGANIC_MATTER ) result = calc_ptf_ferrerjulia2004_calcaric_regosol_texture_organic_matter(**inputs, out=None) - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_calcaric_regosol_texture_organic_matter_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_FERRERJULIA2004_CALCARIC_REGOSOL_TEXTURE_ORGANIC_MATTER ) result = calc_ptf_ferrerjulia2004_calcaric_regosol_texture_organic_matter(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) CASES_CALC_PTF_FERRERJULIA2004_DYSTRIC_REGOSOL_SAND = [ - ({'sand': 50.0}, {'k_sat': 12.081401313183196}, 1e-12, 1e-12), + ({'sand': 50.0}, {'k_sat': 12.081401313183196}), +] +CASES_CALC_PTF_FERRERJULIA2004_DYSTRIC_REGOSOL_SAND_IDS = [ + 'reviewer_reference_vector', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_FERRERJULIA2004_DYSTRIC_REGOSOL_SAND + ('inputs', 'expected'), + CASES_CALC_PTF_FERRERJULIA2004_DYSTRIC_REGOSOL_SAND, + ids=CASES_CALC_PTF_FERRERJULIA2004_DYSTRIC_REGOSOL_SAND_IDS, ) -def test_calc_ptf_ferrerjulia2004_dystric_regosol_sand_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_ferrerjulia2004_dystric_regosol_sand_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_ferrerjulia2004_dystric_regosol_sand(**inputs) - assert result == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result, + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_dystric_regosol_sand_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_FERRERJULIA2004_DYSTRIC_REGOSOL_SAND ) result = calc_ptf_ferrerjulia2004_dystric_regosol_sand(**inputs, out=None) - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_dystric_regosol_sand_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( - CASES_CALC_PTF_FERRERJULIA2004_DYSTRIC_REGOSOL_SAND - ) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_FERRERJULIA2004_DYSTRIC_REGOSOL_SAND) result = calc_ptf_ferrerjulia2004_dystric_regosol_sand(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) CASES_CALC_PTF_FERRERJULIA2004_DYSTRIC_REGOSOL_TEXTURE_ORGANIC_MATTER = [ - ({'clay': 25.0, 'organic_matter': 2.5, 'sand': 50.0}, {'k_sat': 17.1335}, 1e-12, 1e-12), + ({'clay': 25.0, 'organic_matter': 2.5, 'sand': 50.0}, {'k_sat': 17.1335}), +] +CASES_CALC_PTF_FERRERJULIA2004_DYSTRIC_REGOSOL_TEXTURE_ORGANIC_MATTER_IDS = [ + 'reviewer_reference_vector', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), + ('inputs', 'expected'), CASES_CALC_PTF_FERRERJULIA2004_DYSTRIC_REGOSOL_TEXTURE_ORGANIC_MATTER, + ids=CASES_CALC_PTF_FERRERJULIA2004_DYSTRIC_REGOSOL_TEXTURE_ORGANIC_MATTER_IDS, ) -def test_calc_ptf_ferrerjulia2004_dystric_regosol_texture_organic_matter_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_ferrerjulia2004_dystric_regosol_texture_organic_matter_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_ferrerjulia2004_dystric_regosol_texture_organic_matter(**inputs) - assert result == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result, + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_dystric_regosol_texture_organic_matter_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_FERRERJULIA2004_DYSTRIC_REGOSOL_TEXTURE_ORGANIC_MATTER ) result = calc_ptf_ferrerjulia2004_dystric_regosol_texture_organic_matter(**inputs, out=None) - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_dystric_regosol_texture_organic_matter_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_FERRERJULIA2004_DYSTRIC_REGOSOL_TEXTURE_ORGANIC_MATTER ) result = calc_ptf_ferrerjulia2004_dystric_regosol_texture_organic_matter(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) CASES_CALC_PTF_FERRERJULIA2004_EUTRIC_REGOSOL_SAND = [ - ({'sand': 50.0}, {'k_sat': 13.674049986082728}, 1e-12, 1e-12), + ({'sand': 50.0}, {'k_sat': 13.674049986082728}), +] +CASES_CALC_PTF_FERRERJULIA2004_EUTRIC_REGOSOL_SAND_IDS = [ + 'reviewer_reference_vector', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_FERRERJULIA2004_EUTRIC_REGOSOL_SAND + ('inputs', 'expected'), + CASES_CALC_PTF_FERRERJULIA2004_EUTRIC_REGOSOL_SAND, + ids=CASES_CALC_PTF_FERRERJULIA2004_EUTRIC_REGOSOL_SAND_IDS, ) -def test_calc_ptf_ferrerjulia2004_eutric_regosol_sand_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_ferrerjulia2004_eutric_regosol_sand_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_ferrerjulia2004_eutric_regosol_sand(**inputs) - assert result == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result, + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_eutric_regosol_sand_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( - CASES_CALC_PTF_FERRERJULIA2004_EUTRIC_REGOSOL_SAND - ) + inputs, expected, _out = prepare_vector_case(CASES_CALC_PTF_FERRERJULIA2004_EUTRIC_REGOSOL_SAND) result = calc_ptf_ferrerjulia2004_eutric_regosol_sand(**inputs, out=None) - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_eutric_regosol_sand_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( - CASES_CALC_PTF_FERRERJULIA2004_EUTRIC_REGOSOL_SAND - ) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_FERRERJULIA2004_EUTRIC_REGOSOL_SAND) result = calc_ptf_ferrerjulia2004_eutric_regosol_sand(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) CASES_CALC_PTF_FERRERJULIA2004_EUTRIC_REGOSOL_TEXTURE_ORGANIC_MATTER = [ - ({'clay': 25.0, 'organic_matter': 2.5, 'sand': 50.0}, {'k_sat': 24.06375}, 1e-12, 1e-12), + ({'clay': 25.0, 'organic_matter': 2.5, 'sand': 50.0}, {'k_sat': 24.06375}), +] +CASES_CALC_PTF_FERRERJULIA2004_EUTRIC_REGOSOL_TEXTURE_ORGANIC_MATTER_IDS = [ + 'reviewer_reference_vector', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), + ('inputs', 'expected'), CASES_CALC_PTF_FERRERJULIA2004_EUTRIC_REGOSOL_TEXTURE_ORGANIC_MATTER, + ids=CASES_CALC_PTF_FERRERJULIA2004_EUTRIC_REGOSOL_TEXTURE_ORGANIC_MATTER_IDS, ) -def test_calc_ptf_ferrerjulia2004_eutric_regosol_texture_organic_matter_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_ferrerjulia2004_eutric_regosol_texture_organic_matter_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_ferrerjulia2004_eutric_regosol_texture_organic_matter(**inputs) - assert result == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result, + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_eutric_regosol_texture_organic_matter_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_FERRERJULIA2004_EUTRIC_REGOSOL_TEXTURE_ORGANIC_MATTER ) result = calc_ptf_ferrerjulia2004_eutric_regosol_texture_organic_matter(**inputs, out=None) - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_eutric_regosol_texture_organic_matter_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_FERRERJULIA2004_EUTRIC_REGOSOL_TEXTURE_ORGANIC_MATTER ) result = calc_ptf_ferrerjulia2004_eutric_regosol_texture_organic_matter(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) CASES_CALC_PTF_FERRERJULIA2004_RENDZINA_SAND = [ - ({'sand': 50.0}, {'k_sat': 12.515763717130312}, 1e-12, 1e-12), + ({'sand': 50.0}, {'k_sat': 12.515763717130312}), +] +CASES_CALC_PTF_FERRERJULIA2004_RENDZINA_SAND_IDS = [ + 'reviewer_reference_vector', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_FERRERJULIA2004_RENDZINA_SAND + ('inputs', 'expected'), + CASES_CALC_PTF_FERRERJULIA2004_RENDZINA_SAND, + ids=CASES_CALC_PTF_FERRERJULIA2004_RENDZINA_SAND_IDS, ) -def test_calc_ptf_ferrerjulia2004_rendzina_sand_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_ferrerjulia2004_rendzina_sand_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_ferrerjulia2004_rendzina_sand(**inputs) - assert result == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result, + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_rendzina_sand_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( - CASES_CALC_PTF_FERRERJULIA2004_RENDZINA_SAND - ) + inputs, expected, _out = prepare_vector_case(CASES_CALC_PTF_FERRERJULIA2004_RENDZINA_SAND) result = calc_ptf_ferrerjulia2004_rendzina_sand(**inputs, out=None) - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_rendzina_sand_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( - CASES_CALC_PTF_FERRERJULIA2004_RENDZINA_SAND - ) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_FERRERJULIA2004_RENDZINA_SAND) result = calc_ptf_ferrerjulia2004_rendzina_sand(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) CASES_CALC_PTF_FERRERJULIA2004_RENDZINA_TEXTURE_ORGANIC_MATTER = [ - ({'clay': 25.0, 'organic_matter': 2.5, 'sand': 50.0}, {'k_sat': 16.2905}, 1e-12, 1e-12), + ({'clay': 25.0, 'organic_matter': 2.5, 'sand': 50.0}, {'k_sat': 16.2905}), +] +CASES_CALC_PTF_FERRERJULIA2004_RENDZINA_TEXTURE_ORGANIC_MATTER_IDS = [ + 'reviewer_reference_vector', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), + ('inputs', 'expected'), CASES_CALC_PTF_FERRERJULIA2004_RENDZINA_TEXTURE_ORGANIC_MATTER, + ids=CASES_CALC_PTF_FERRERJULIA2004_RENDZINA_TEXTURE_ORGANIC_MATTER_IDS, ) -def test_calc_ptf_ferrerjulia2004_rendzina_texture_organic_matter_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_ferrerjulia2004_rendzina_texture_organic_matter_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_ferrerjulia2004_rendzina_texture_organic_matter(**inputs) - assert result == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result, + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_rendzina_texture_organic_matter_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_FERRERJULIA2004_RENDZINA_TEXTURE_ORGANIC_MATTER ) result = calc_ptf_ferrerjulia2004_rendzina_texture_organic_matter(**inputs, out=None) - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_rendzina_texture_organic_matter_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_FERRERJULIA2004_RENDZINA_TEXTURE_ORGANIC_MATTER ) result = calc_ptf_ferrerjulia2004_rendzina_texture_organic_matter(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) CASES_CALC_PTF_FERRERJULIA2004_GLEYIC_SOLONCHAK_SAND = [ - ({'sand': 50.0}, {'k_sat': 4.883324693640427}, 1e-12, 1e-12), + ({'sand': 50.0}, {'k_sat': 4.883324693640427}), +] +CASES_CALC_PTF_FERRERJULIA2004_GLEYIC_SOLONCHAK_SAND_IDS = [ + 'reviewer_reference_vector', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_FERRERJULIA2004_GLEYIC_SOLONCHAK_SAND + ('inputs', 'expected'), + CASES_CALC_PTF_FERRERJULIA2004_GLEYIC_SOLONCHAK_SAND, + ids=CASES_CALC_PTF_FERRERJULIA2004_GLEYIC_SOLONCHAK_SAND_IDS, ) -def test_calc_ptf_ferrerjulia2004_gleyic_solonchak_sand_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_ferrerjulia2004_gleyic_solonchak_sand_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_ferrerjulia2004_gleyic_solonchak_sand(**inputs) - assert result == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result, + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_gleyic_solonchak_sand_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_FERRERJULIA2004_GLEYIC_SOLONCHAK_SAND ) result = calc_ptf_ferrerjulia2004_gleyic_solonchak_sand(**inputs, out=None) - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_gleyic_solonchak_sand_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_FERRERJULIA2004_GLEYIC_SOLONCHAK_SAND ) result = calc_ptf_ferrerjulia2004_gleyic_solonchak_sand(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) CASES_CALC_PTF_FERRERJULIA2004_GLEYIC_SOLONCHAK_TEXTURE_ORGANIC_MATTER = [ - ({'clay': 25.0, 'organic_matter': 2.5, 'sand': 50.0}, {'k_sat': 10.53583}, 1e-12, 1e-12), + ({'clay': 25.0, 'organic_matter': 2.5, 'sand': 50.0}, {'k_sat': 10.53583}), +] +CASES_CALC_PTF_FERRERJULIA2004_GLEYIC_SOLONCHAK_TEXTURE_ORGANIC_MATTER_IDS = [ + 'reviewer_reference_vector', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), + ('inputs', 'expected'), CASES_CALC_PTF_FERRERJULIA2004_GLEYIC_SOLONCHAK_TEXTURE_ORGANIC_MATTER, + ids=CASES_CALC_PTF_FERRERJULIA2004_GLEYIC_SOLONCHAK_TEXTURE_ORGANIC_MATTER_IDS, ) -def test_calc_ptf_ferrerjulia2004_gleyic_solonchak_texture_organic_matter_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_ferrerjulia2004_gleyic_solonchak_texture_organic_matter_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_ferrerjulia2004_gleyic_solonchak_texture_organic_matter(**inputs) - assert result == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result, + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_gleyic_solonchak_texture_organic_matter_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_FERRERJULIA2004_GLEYIC_SOLONCHAK_TEXTURE_ORGANIC_MATTER ) result = calc_ptf_ferrerjulia2004_gleyic_solonchak_texture_organic_matter(**inputs, out=None) - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_gleyic_solonchak_texture_organic_matter_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_FERRERJULIA2004_GLEYIC_SOLONCHAK_TEXTURE_ORGANIC_MATTER ) result = calc_ptf_ferrerjulia2004_gleyic_solonchak_texture_organic_matter(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) CASES_CALC_PTF_FERRERJULIA2004_GENERAL_SAND = [ - ({'sand': 50.0}, {'k_sat': 10.714718864969111}, 1e-12, 1e-12), + ({'sand': 50.0}, {'k_sat': 10.714718864969111}), +] +CASES_CALC_PTF_FERRERJULIA2004_GENERAL_SAND_IDS = [ + 'reviewer_reference_vector', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_FERRERJULIA2004_GENERAL_SAND + ('inputs', 'expected'), + CASES_CALC_PTF_FERRERJULIA2004_GENERAL_SAND, + ids=CASES_CALC_PTF_FERRERJULIA2004_GENERAL_SAND_IDS, ) -def test_calc_ptf_ferrerjulia2004_general_sand_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_ferrerjulia2004_general_sand_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_ferrerjulia2004_general_sand(**inputs) - assert result == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result, + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_general_sand_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( - CASES_CALC_PTF_FERRERJULIA2004_GENERAL_SAND - ) + inputs, expected, _out = prepare_vector_case(CASES_CALC_PTF_FERRERJULIA2004_GENERAL_SAND) result = calc_ptf_ferrerjulia2004_general_sand(**inputs, out=None) - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_general_sand_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( - CASES_CALC_PTF_FERRERJULIA2004_GENERAL_SAND - ) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_FERRERJULIA2004_GENERAL_SAND) result = calc_ptf_ferrerjulia2004_general_sand(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) CASES_CALC_PTF_FERRERJULIA2004_GENERAL_TEXTURE_ORGANIC_MATTER = [ - ({'clay': 25.0, 'organic_matter': 2.5, 'sand': 50.0}, {'k_sat': 20.06325}, 1e-12, 1e-12), + ({'clay': 25.0, 'organic_matter': 2.5, 'sand': 50.0}, {'k_sat': 20.06325}), +] +CASES_CALC_PTF_FERRERJULIA2004_GENERAL_TEXTURE_ORGANIC_MATTER_IDS = [ + 'reviewer_reference_vector', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), + ('inputs', 'expected'), CASES_CALC_PTF_FERRERJULIA2004_GENERAL_TEXTURE_ORGANIC_MATTER, + ids=CASES_CALC_PTF_FERRERJULIA2004_GENERAL_TEXTURE_ORGANIC_MATTER_IDS, ) -def test_calc_ptf_ferrerjulia2004_general_texture_organic_matter_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_ferrerjulia2004_general_texture_organic_matter_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_ferrerjulia2004_general_texture_organic_matter(**inputs) - assert result == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result, + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_general_texture_organic_matter_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_FERRERJULIA2004_GENERAL_TEXTURE_ORGANIC_MATTER ) result = calc_ptf_ferrerjulia2004_general_texture_organic_matter(**inputs, out=None) - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_ptf_ferrerjulia2004_general_texture_organic_matter_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_FERRERJULIA2004_GENERAL_TEXTURE_ORGANIC_MATTER ) result = calc_ptf_ferrerjulia2004_general_texture_organic_matter(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) diff --git a/targets/ptfkit-py/tests/test_hodnett2002.py b/targets/ptfkit-py/tests/test_hodnett2002.py index b4a6cd5..53f62ce 100644 --- a/targets/ptfkit-py/tests/test_hodnett2002.py +++ b/targets/ptfkit-py/tests/test_hodnett2002.py @@ -3,7 +3,7 @@ import pytest -from _helpers import prepare_vector_case +from _helpers import assert_close, prepare_vector_case from ptfkit.hodnett2002 import Hodnett2002PTFResult, calc_ptf_hodnett2002 @@ -21,46 +21,139 @@ { 'alpha': 0.245183615037873, 'n': 1.36739326352383, - 'theta_r': 0.21344216, 'theta_s': 0.4993353, + 'theta_r': 0.21344216, }, - 1e-12, - 1e-12, ), ] +CASES_CALC_PTF_HODNETT2002_IDS = [ + 'calibration_dataset_mean_properties', +] -@pytest.mark.parametrize(('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_HODNETT2002) -def test_calc_ptf_hodnett2002_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float -): +@pytest.mark.parametrize( + ('inputs', 'expected'), CASES_CALC_PTF_HODNETT2002, ids=CASES_CALC_PTF_HODNETT2002_IDS +) +def test_calc_ptf_hodnett2002_verification(inputs: dict[str, float], expected: dict[str, float]): result = calc_ptf_hodnett2002(**inputs) - assert result.alpha == pytest.approx(expected['alpha'], rel=rtol, abs=atol) - assert result.n == pytest.approx(expected['n'], rel=rtol, abs=atol) - assert result.theta_s == pytest.approx(expected['theta_s'], rel=rtol, abs=atol) - assert result.theta_r == pytest.approx(expected['theta_r'], rel=rtol, abs=atol) + assert_close( + result.alpha, + expected['alpha'], + absolute=1e-5, + relative=0.0, + quantity='van_genuchten_alpha', + unit='inverse_kilopascal', + source='registry', + ) + assert_close( + result.n, + expected['n'], + absolute=0.001, + relative=0.0, + quantity='van_genuchten_n', + unit='dimensionless', + source='registry', + ) + assert_close( + result.theta_s, + expected['theta_s'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_r, + expected['theta_r'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) def test_calc_ptf_hodnett2002_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( - CASES_CALC_PTF_HODNETT2002, Hodnett2002PTFResult - ) + inputs, expected, _out = prepare_vector_case(CASES_CALC_PTF_HODNETT2002, Hodnett2002PTFResult) result = calc_ptf_hodnett2002(**inputs, out=None) - assert result.alpha[0] == pytest.approx(expected['alpha'], rel=rtol, abs=atol) - assert result.n[0] == pytest.approx(expected['n'], rel=rtol, abs=atol) - assert result.theta_s[0] == pytest.approx(expected['theta_s'], rel=rtol, abs=atol) - assert result.theta_r[0] == pytest.approx(expected['theta_r'], rel=rtol, abs=atol) + assert_close( + result.alpha[0], + expected['alpha'], + absolute=1e-5, + relative=0.0, + quantity='van_genuchten_alpha', + unit='inverse_kilopascal', + source='registry', + ) + assert_close( + result.n[0], + expected['n'], + absolute=0.001, + relative=0.0, + quantity='van_genuchten_n', + unit='dimensionless', + source='registry', + ) + assert_close( + result.theta_s[0], + expected['theta_s'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_r[0], + expected['theta_r'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) def test_calc_ptf_hodnett2002_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( - CASES_CALC_PTF_HODNETT2002, Hodnett2002PTFResult - ) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_HODNETT2002, Hodnett2002PTFResult) result = calc_ptf_hodnett2002(**inputs, out=out) for actual, expected_out in zip(result, out, strict=True): assert actual is expected_out - assert result.alpha[0] == pytest.approx(expected['alpha'], rel=rtol, abs=atol) - assert result.n[0] == pytest.approx(expected['n'], rel=rtol, abs=atol) - assert result.theta_s[0] == pytest.approx(expected['theta_s'], rel=rtol, abs=atol) - assert result.theta_r[0] == pytest.approx(expected['theta_r'], rel=rtol, abs=atol) + assert_close( + result.alpha[0], + expected['alpha'], + absolute=1e-5, + relative=0.0, + quantity='van_genuchten_alpha', + unit='inverse_kilopascal', + source='registry', + ) + assert_close( + result.n[0], + expected['n'], + absolute=0.001, + relative=0.0, + quantity='van_genuchten_n', + unit='dimensionless', + source='registry', + ) + assert_close( + result.theta_s[0], + expected['theta_s'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_r[0], + expected['theta_r'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) diff --git a/targets/ptfkit-py/tests/test_jabro1992.py b/targets/ptfkit-py/tests/test_jabro1992.py index 78d2388..428b3a4 100644 --- a/targets/ptfkit-py/tests/test_jabro1992.py +++ b/targets/ptfkit-py/tests/test_jabro1992.py @@ -3,55 +3,65 @@ import pytest -from _helpers import prepare_vector_case +from _helpers import assert_close, prepare_vector_case from ptfkit.jabro1992 import calc_ptf_jabro1992 CASES_CALC_PTF_JABRO1992 = [ - ( - {'bulk_density': 1.26, 'clay': 5.0, 'silt': 10.0}, - {'k_sat': 0.0003849640675896946}, - 1e-8, - 1e-12, - ), - ( - {'bulk_density': 1.42, 'clay': 11.05, 'silt': 38.72}, - {'k_sat': 9.804037952717678e-6}, - 1e-8, - 1e-12, - ), - ( - {'bulk_density': 1.97, 'clay': 30.0, 'silt': 52.0}, - {'k_sat': 7.292435947882127e-9}, - 1e-8, - 1e-12, - ), - ( - {'bulk_density': 1.61, 'clay': 44.0, 'silt': 0.2}, - {'k_sat': 2.032824027706267e-5}, - 1e-8, - 1e-12, - ), + ({'bulk_density': 1.26, 'clay': 5.0, 'silt': 10.0}, {'k_sat': 0.0003849640675896946}), + ({'bulk_density': 1.42, 'clay': 11.05, 'silt': 38.72}, {'k_sat': 9.804037952717678e-6}), + ({'bulk_density': 1.97, 'clay': 30.0, 'silt': 52.0}, {'k_sat': 7.292435947882127e-9}), + ({'bulk_density': 1.61, 'clay': 44.0, 'silt': 0.2}, {'k_sat': 2.032824027706267e-5}), +] +CASES_CALC_PTF_JABRO1992_IDS = [ + 'loamy_sand_min_bd', + 'loam', + 'silty_clay_loam_max_silt', + 'sandy_clay_min_silt_max_clay', ] -@pytest.mark.parametrize(('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_JABRO1992) -def test_calc_ptf_jabro1992_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float -): +@pytest.mark.parametrize( + ('inputs', 'expected'), CASES_CALC_PTF_JABRO1992, ids=CASES_CALC_PTF_JABRO1992_IDS +) +def test_calc_ptf_jabro1992_verification(inputs: dict[str, float], expected: dict[str, float]): result = calc_ptf_jabro1992(**inputs) - assert result == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result, + expected['k_sat'], + absolute=1e-10, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='meter_per_second', + source='registry', + ) def test_calc_ptf_jabro1992_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case(CASES_CALC_PTF_JABRO1992) + inputs, expected, _out = prepare_vector_case(CASES_CALC_PTF_JABRO1992) result = calc_ptf_jabro1992(**inputs, out=None) - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=1e-10, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='meter_per_second', + source='registry', + ) def test_calc_ptf_jabro1992_out(): - inputs, expected, rtol, atol, out = prepare_vector_case(CASES_CALC_PTF_JABRO1992) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_JABRO1992) result = calc_ptf_jabro1992(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=1e-10, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='meter_per_second', + source='registry', + ) diff --git a/targets/ptfkit-py/tests/test_li2007.py b/targets/ptfkit-py/tests/test_li2007.py index 39bca0d..7b9a126 100644 --- a/targets/ptfkit-py/tests/test_li2007.py +++ b/targets/ptfkit-py/tests/test_li2007.py @@ -3,7 +3,7 @@ import pytest -from _helpers import prepare_vector_case +from _helpers import assert_close, prepare_vector_case from ptfkit.li2007 import Li2007PTFResult, calc_ptf_li2007 @@ -11,13 +11,11 @@ ( {'bulk_density': 1.2, 'clay': 5.0, 'sand': 85.0, 'silt': 10.0, 'soil_organic_matter': 0.21}, { + 'theta_s': 0.5256803583157499, 'a_vg': 0.9491464758307142, - 'k_sat': 6.549110367333547e-6, 'n_vg': 1.1657804980997006, - 'theta_s': 0.5256803583157499, + 'k_sat': 6.549110367333547e-6, }, - 1e-8, - 1e-12, ), ( { @@ -28,13 +26,11 @@ 'soil_organic_matter': 0.65, }, { + 'theta_s': 0.49659526127697506, 'a_vg': 0.009519989841950734, - 'k_sat': 4.5117324656202257e-7, 'n_vg': 1.1806286355149054, - 'theta_s': 0.49659526127697506, + 'k_sat': 4.5117324656202257e-7, }, - 1e-8, - 1e-12, ), ( { @@ -45,44 +41,143 @@ 'soil_organic_matter': 1.02, }, { + 'theta_s': 0.4053061510618609, 'a_vg': 0.0018530400762371828, - 'k_sat': 1.5151432632107234e-6, 'n_vg': 1.2080428739797433, - 'theta_s': 0.4053061510618609, + 'k_sat': 1.5151432632107234e-6, }, - 1e-8, - 1e-12, ), ] +CASES_CALC_PTF_LI2007_IDS = [ + 'loamy_sand', + 'loam', + 'silty_clay', +] -@pytest.mark.parametrize(('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_LI2007) -def test_calc_ptf_li2007_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float -): +@pytest.mark.parametrize( + ('inputs', 'expected'), CASES_CALC_PTF_LI2007, ids=CASES_CALC_PTF_LI2007_IDS +) +def test_calc_ptf_li2007_verification(inputs: dict[str, float], expected: dict[str, float]): result = calc_ptf_li2007(**inputs) - assert result.theta_s == pytest.approx(expected['theta_s'], rel=rtol, abs=atol) - assert result.a_vg == pytest.approx(expected['a_vg'], rel=rtol, abs=atol) - assert result.n_vg == pytest.approx(expected['n_vg'], rel=rtol, abs=atol) - assert result.k_sat == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result.theta_s, + expected['theta_s'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.a_vg, + expected['a_vg'], + absolute=1e-5, + relative=0.0, + quantity='van_genuchten_alpha', + unit='inverse_centimeter', + source='registry', + ) + assert_close( + result.n_vg, + expected['n_vg'], + absolute=0.001, + relative=0.0, + quantity='van_genuchten_n', + unit='dimensionless', + source='registry', + ) + assert_close( + result.k_sat, + expected['k_sat'], + absolute=1e-10, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='meter_per_second', + source='registry', + ) def test_calc_ptf_li2007_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case(CASES_CALC_PTF_LI2007, Li2007PTFResult) + inputs, expected, _out = prepare_vector_case(CASES_CALC_PTF_LI2007, Li2007PTFResult) result = calc_ptf_li2007(**inputs, out=None) - assert result.theta_s[0] == pytest.approx(expected['theta_s'], rel=rtol, abs=atol) - assert result.a_vg[0] == pytest.approx(expected['a_vg'], rel=rtol, abs=atol) - assert result.n_vg[0] == pytest.approx(expected['n_vg'], rel=rtol, abs=atol) - assert result.k_sat[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result.theta_s[0], + expected['theta_s'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.a_vg[0], + expected['a_vg'], + absolute=1e-5, + relative=0.0, + quantity='van_genuchten_alpha', + unit='inverse_centimeter', + source='registry', + ) + assert_close( + result.n_vg[0], + expected['n_vg'], + absolute=0.001, + relative=0.0, + quantity='van_genuchten_n', + unit='dimensionless', + source='registry', + ) + assert_close( + result.k_sat[0], + expected['k_sat'], + absolute=1e-10, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='meter_per_second', + source='registry', + ) def test_calc_ptf_li2007_out(): - inputs, expected, rtol, atol, out = prepare_vector_case(CASES_CALC_PTF_LI2007, Li2007PTFResult) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_LI2007, Li2007PTFResult) result = calc_ptf_li2007(**inputs, out=out) for actual, expected_out in zip(result, out, strict=True): assert actual is expected_out - assert result.theta_s[0] == pytest.approx(expected['theta_s'], rel=rtol, abs=atol) - assert result.a_vg[0] == pytest.approx(expected['a_vg'], rel=rtol, abs=atol) - assert result.n_vg[0] == pytest.approx(expected['n_vg'], rel=rtol, abs=atol) - assert result.k_sat[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result.theta_s[0], + expected['theta_s'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.a_vg[0], + expected['a_vg'], + absolute=1e-5, + relative=0.0, + quantity='van_genuchten_alpha', + unit='inverse_centimeter', + source='registry', + ) + assert_close( + result.n_vg[0], + expected['n_vg'], + absolute=0.001, + relative=0.0, + quantity='van_genuchten_n', + unit='dimensionless', + source='registry', + ) + assert_close( + result.k_sat[0], + expected['k_sat'], + absolute=1e-10, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='meter_per_second', + source='registry', + ) diff --git a/targets/ptfkit-py/tests/test_mayr1999.py b/targets/ptfkit-py/tests/test_mayr1999.py index 5a36bbb..7656fda 100644 --- a/targets/ptfkit-py/tests/test_mayr1999.py +++ b/targets/ptfkit-py/tests/test_mayr1999.py @@ -3,7 +3,7 @@ import pytest -from _helpers import prepare_vector_case +from _helpers import assert_close, prepare_vector_case from ptfkit.mayr1999 import Mayr1999PTFResult, calc_ptf_mayr1999 @@ -11,52 +11,119 @@ ( {'bulk_density': 1.3, 'clay': 20.0, 'organic_carbon': 2.0, 'sand': 40.0, 'silt': 40.0}, {'a_hc': 9.19922782578422, 'b_hc': 8.52432443785475, 'theta_s': 0.48733161333}, - 1e-12, - 1e-12, ), ( {'bulk_density': 1.2, 'clay': 60.0, 'organic_carbon': 2.0, 'sand': 20.0, 'silt': 20.0}, {'a_hc': 14.5305137640911, 'b_hc': 18.9375141190925, 'theta_s': 0.52145684162}, - 1e-12, - 1e-12, ), ( {'bulk_density': 1.5, 'clay': 5.0, 'organic_carbon': 1.0, 'sand': 90.0, 'silt': 5.0}, {'a_hc': 2.40216699550362, 'b_hc': 3.2690464531591, 'theta_s': 0.4209756915}, - 1e-12, - 1e-12, ), ] +CASES_CALC_PTF_MAYR1999_IDS = [ + 'representative_loam', + 'representative_clay', + 'representative_sand', +] -@pytest.mark.parametrize(('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_MAYR1999) -def test_calc_ptf_mayr1999_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float -): +@pytest.mark.parametrize( + ('inputs', 'expected'), CASES_CALC_PTF_MAYR1999, ids=CASES_CALC_PTF_MAYR1999_IDS +) +def test_calc_ptf_mayr1999_verification(inputs: dict[str, float], expected: dict[str, float]): result = calc_ptf_mayr1999(**inputs) - assert result.a_hc == pytest.approx(expected['a_hc'], rel=rtol, abs=atol) - assert result.b_hc == pytest.approx(expected['b_hc'], rel=rtol, abs=atol) - assert result.theta_s == pytest.approx(expected['theta_s'], rel=rtol, abs=atol) + assert_close( + result.a_hc, + expected['a_hc'], + absolute=0.01, + relative=0.0, + quantity='matric_potential', + unit='centimeter_water_head', + source='registry', + ) + assert_close( + result.b_hc, + expected['b_hc'], + absolute=0.001, + relative=0.0, + quantity='hutson_cass_b', + unit='dimensionless', + source='registry', + ) + assert_close( + result.theta_s, + expected['theta_s'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) def test_calc_ptf_mayr1999_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( - CASES_CALC_PTF_MAYR1999, Mayr1999PTFResult - ) + inputs, expected, _out = prepare_vector_case(CASES_CALC_PTF_MAYR1999, Mayr1999PTFResult) result = calc_ptf_mayr1999(**inputs, out=None) - assert result.a_hc[0] == pytest.approx(expected['a_hc'], rel=rtol, abs=atol) - assert result.b_hc[0] == pytest.approx(expected['b_hc'], rel=rtol, abs=atol) - assert result.theta_s[0] == pytest.approx(expected['theta_s'], rel=rtol, abs=atol) + assert_close( + result.a_hc[0], + expected['a_hc'], + absolute=0.01, + relative=0.0, + quantity='matric_potential', + unit='centimeter_water_head', + source='registry', + ) + assert_close( + result.b_hc[0], + expected['b_hc'], + absolute=0.001, + relative=0.0, + quantity='hutson_cass_b', + unit='dimensionless', + source='registry', + ) + assert_close( + result.theta_s[0], + expected['theta_s'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) def test_calc_ptf_mayr1999_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( - CASES_CALC_PTF_MAYR1999, Mayr1999PTFResult - ) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_MAYR1999, Mayr1999PTFResult) result = calc_ptf_mayr1999(**inputs, out=out) for actual, expected_out in zip(result, out, strict=True): assert actual is expected_out - assert result.a_hc[0] == pytest.approx(expected['a_hc'], rel=rtol, abs=atol) - assert result.b_hc[0] == pytest.approx(expected['b_hc'], rel=rtol, abs=atol) - assert result.theta_s[0] == pytest.approx(expected['theta_s'], rel=rtol, abs=atol) + assert_close( + result.a_hc[0], + expected['a_hc'], + absolute=0.01, + relative=0.0, + quantity='matric_potential', + unit='centimeter_water_head', + source='registry', + ) + assert_close( + result.b_hc[0], + expected['b_hc'], + absolute=0.001, + relative=0.0, + quantity='hutson_cass_b', + unit='dimensionless', + source='registry', + ) + assert_close( + result.theta_s[0], + expected['theta_s'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) diff --git a/targets/ptfkit-py/tests/test_oosterveld1980.py b/targets/ptfkit-py/tests/test_oosterveld1980.py index 2a4a4b3..8c352dc 100644 --- a/targets/ptfkit-py/tests/test_oosterveld1980.py +++ b/targets/ptfkit-py/tests/test_oosterveld1980.py @@ -3,7 +3,7 @@ import pytest -from _helpers import prepare_vector_case +from _helpers import assert_close, prepare_vector_case from ptfkit.oosterveld1980 import ( calc_ptf_oosterveld1980_available_water, calc_ptf_oosterveld1980_field_capacity, @@ -14,183 +14,300 @@ CASES_CALC_PTF_OOSTERVELD1980_FIELD_CAPACITY_TENSION = [ - ({'clay': 6.6}, {'field_capacity_tension': 11.8540994164288}, 1e-12, 1e-12), + ({'clay': 6.6}, {'field_capacity_tension': 11.8540994164288}), +] +CASES_CALC_PTF_OOSTERVELD1980_FIELD_CAPACITY_TENSION_IDS = [ + 'table_1_loamy_sand', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_OOSTERVELD1980_FIELD_CAPACITY_TENSION + ('inputs', 'expected'), + CASES_CALC_PTF_OOSTERVELD1980_FIELD_CAPACITY_TENSION, + ids=CASES_CALC_PTF_OOSTERVELD1980_FIELD_CAPACITY_TENSION_IDS, ) -def test_calc_ptf_oosterveld1980_field_capacity_tension_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_oosterveld1980_field_capacity_tension_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_oosterveld1980_field_capacity_tension(**inputs) - assert result == pytest.approx(expected['field_capacity_tension'], rel=rtol, abs=atol) + assert_close( + result, + expected['field_capacity_tension'], + absolute=0.01, + relative=0.0, + quantity='matric_potential', + unit='kilopascal', + source='registry', + ) def test_calc_ptf_oosterveld1980_field_capacity_tension_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_OOSTERVELD1980_FIELD_CAPACITY_TENSION ) result = calc_ptf_oosterveld1980_field_capacity_tension(**inputs, out=None) - assert result[0] == pytest.approx(expected['field_capacity_tension'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['field_capacity_tension'], + absolute=0.01, + relative=0.0, + quantity='matric_potential', + unit='kilopascal', + source='registry', + ) def test_calc_ptf_oosterveld1980_field_capacity_tension_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_OOSTERVELD1980_FIELD_CAPACITY_TENSION ) result = calc_ptf_oosterveld1980_field_capacity_tension(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['field_capacity_tension'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['field_capacity_tension'], + absolute=0.01, + relative=0.0, + quantity='matric_potential', + unit='kilopascal', + source='registry', + ) CASES_CALC_PTF_OOSTERVELD1980_RETENTION = [ ( {'clay': 6.6, 'mean_depth': 105.0, 'sand': 86.3, 'tension': 11.8}, {'moisture_content': 8.2782468100413}, - 1e-12, - 1e-12, ), ] +CASES_CALC_PTF_OOSTERVELD1980_RETENTION_IDS = [ + 'table_1_loamy_sand_inputs', +] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_OOSTERVELD1980_RETENTION + ('inputs', 'expected'), + CASES_CALC_PTF_OOSTERVELD1980_RETENTION, + ids=CASES_CALC_PTF_OOSTERVELD1980_RETENTION_IDS, ) -def test_calc_ptf_oosterveld1980_retention_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_oosterveld1980_retention_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_oosterveld1980_retention(**inputs) - assert result == pytest.approx(expected['moisture_content'], rel=rtol, abs=atol) + assert_close( + result, + expected['moisture_content'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) def test_calc_ptf_oosterveld1980_retention_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( - CASES_CALC_PTF_OOSTERVELD1980_RETENTION - ) + inputs, expected, _out = prepare_vector_case(CASES_CALC_PTF_OOSTERVELD1980_RETENTION) result = calc_ptf_oosterveld1980_retention(**inputs, out=None) - assert result[0] == pytest.approx(expected['moisture_content'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['moisture_content'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) def test_calc_ptf_oosterveld1980_retention_out(): - inputs, expected, rtol, atol, out = prepare_vector_case(CASES_CALC_PTF_OOSTERVELD1980_RETENTION) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_OOSTERVELD1980_RETENTION) result = calc_ptf_oosterveld1980_retention(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['moisture_content'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['moisture_content'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) CASES_CALC_PTF_OOSTERVELD1980_FIELD_CAPACITY = [ ( {'clay': 6.6, 'mean_depth': 105.0, 'sand': 86.3}, {'field_capacity_moisture': 8.14707947394088}, - 1e-12, - 1e-12, ), ] +CASES_CALC_PTF_OOSTERVELD1980_FIELD_CAPACITY_IDS = [ + 'table_1_loamy_sand', +] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_OOSTERVELD1980_FIELD_CAPACITY + ('inputs', 'expected'), + CASES_CALC_PTF_OOSTERVELD1980_FIELD_CAPACITY, + ids=CASES_CALC_PTF_OOSTERVELD1980_FIELD_CAPACITY_IDS, ) -def test_calc_ptf_oosterveld1980_field_capacity_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_oosterveld1980_field_capacity_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_oosterveld1980_field_capacity(**inputs) - assert result == pytest.approx(expected['field_capacity_moisture'], rel=rtol, abs=atol) + assert_close( + result, + expected['field_capacity_moisture'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) def test_calc_ptf_oosterveld1980_field_capacity_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( - CASES_CALC_PTF_OOSTERVELD1980_FIELD_CAPACITY - ) + inputs, expected, _out = prepare_vector_case(CASES_CALC_PTF_OOSTERVELD1980_FIELD_CAPACITY) result = calc_ptf_oosterveld1980_field_capacity(**inputs, out=None) - assert result[0] == pytest.approx(expected['field_capacity_moisture'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['field_capacity_moisture'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) def test_calc_ptf_oosterveld1980_field_capacity_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( - CASES_CALC_PTF_OOSTERVELD1980_FIELD_CAPACITY - ) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_OOSTERVELD1980_FIELD_CAPACITY) result = calc_ptf_oosterveld1980_field_capacity(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['field_capacity_moisture'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['field_capacity_moisture'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) CASES_CALC_PTF_OOSTERVELD1980_WILTING_POINT = [ - ( - {'clay': 6.6, 'mean_depth': 105.0, 'sand': 86.3}, - {'wilting_point_moisture': 1.3942}, - 1e-12, - 1e-12, - ), + ({'clay': 6.6, 'mean_depth': 105.0, 'sand': 86.3}, {'wilting_point_moisture': 1.3942}), +] +CASES_CALC_PTF_OOSTERVELD1980_WILTING_POINT_IDS = [ + 'table_1_loamy_sand', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_OOSTERVELD1980_WILTING_POINT + ('inputs', 'expected'), + CASES_CALC_PTF_OOSTERVELD1980_WILTING_POINT, + ids=CASES_CALC_PTF_OOSTERVELD1980_WILTING_POINT_IDS, ) -def test_calc_ptf_oosterveld1980_wilting_point_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_oosterveld1980_wilting_point_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_oosterveld1980_wilting_point(**inputs) - assert result == pytest.approx(expected['wilting_point_moisture'], rel=rtol, abs=atol) + assert_close( + result, + expected['wilting_point_moisture'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) def test_calc_ptf_oosterveld1980_wilting_point_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( - CASES_CALC_PTF_OOSTERVELD1980_WILTING_POINT - ) + inputs, expected, _out = prepare_vector_case(CASES_CALC_PTF_OOSTERVELD1980_WILTING_POINT) result = calc_ptf_oosterveld1980_wilting_point(**inputs, out=None) - assert result[0] == pytest.approx(expected['wilting_point_moisture'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['wilting_point_moisture'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) def test_calc_ptf_oosterveld1980_wilting_point_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( - CASES_CALC_PTF_OOSTERVELD1980_WILTING_POINT - ) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_OOSTERVELD1980_WILTING_POINT) result = calc_ptf_oosterveld1980_wilting_point(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['wilting_point_moisture'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['wilting_point_moisture'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) CASES_CALC_PTF_OOSTERVELD1980_AVAILABLE_WATER = [ - ( - {'clay': 6.6, 'mean_depth': 105.0, 'sand': 86.3}, - {'available_moisture': 6.75287947394088}, - 1e-12, - 1e-12, - ), + ({'clay': 6.6, 'mean_depth': 105.0, 'sand': 86.3}, {'available_moisture': 6.75287947394088}), +] +CASES_CALC_PTF_OOSTERVELD1980_AVAILABLE_WATER_IDS = [ + 'table_1_loamy_sand', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_OOSTERVELD1980_AVAILABLE_WATER + ('inputs', 'expected'), + CASES_CALC_PTF_OOSTERVELD1980_AVAILABLE_WATER, + ids=CASES_CALC_PTF_OOSTERVELD1980_AVAILABLE_WATER_IDS, ) -def test_calc_ptf_oosterveld1980_available_water_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_oosterveld1980_available_water_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_oosterveld1980_available_water(**inputs) - assert result == pytest.approx(expected['available_moisture'], rel=rtol, abs=atol) + assert_close( + result, + expected['available_moisture'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) def test_calc_ptf_oosterveld1980_available_water_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( - CASES_CALC_PTF_OOSTERVELD1980_AVAILABLE_WATER - ) + inputs, expected, _out = prepare_vector_case(CASES_CALC_PTF_OOSTERVELD1980_AVAILABLE_WATER) result = calc_ptf_oosterveld1980_available_water(**inputs, out=None) - assert result[0] == pytest.approx(expected['available_moisture'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['available_moisture'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) def test_calc_ptf_oosterveld1980_available_water_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( - CASES_CALC_PTF_OOSTERVELD1980_AVAILABLE_WATER - ) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_OOSTERVELD1980_AVAILABLE_WATER) result = calc_ptf_oosterveld1980_available_water(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['available_moisture'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['available_moisture'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) diff --git a/targets/ptfkit-py/tests/test_pidgeon1972.py b/targets/ptfkit-py/tests/test_pidgeon1972.py index ba6407f..5ca2ee2 100644 --- a/targets/ptfkit-py/tests/test_pidgeon1972.py +++ b/targets/ptfkit-py/tests/test_pidgeon1972.py @@ -3,7 +3,7 @@ import pytest -from _helpers import prepare_vector_case +from _helpers import assert_close, prepare_vector_case from ptfkit.pidgeon1972 import ( calc_ptf_pidgeon1972_awc, calc_ptf_pidgeon1972_awc_coarse_sand, @@ -26,579 +26,993 @@ CASES_CALC_PTF_PIDGEON1972_FC = [ - ({'clay': 20.0, 'organic_matter': 2.0, 'silt': 30.0}, {'field_capacity': 21.26}, 1e-12, 1e-12), + ({'clay': 20.0, 'organic_matter': 2.0, 'silt': 30.0}, {'field_capacity': 21.26}), +] +CASES_CALC_PTF_PIDGEON1972_FC_IDS = [ + 'regression_case', ] -@pytest.mark.parametrize(('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_PIDGEON1972_FC) -def test_calc_ptf_pidgeon1972_fc_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float -): +@pytest.mark.parametrize( + ('inputs', 'expected'), CASES_CALC_PTF_PIDGEON1972_FC, ids=CASES_CALC_PTF_PIDGEON1972_FC_IDS +) +def test_calc_ptf_pidgeon1972_fc_verification(inputs: dict[str, float], expected: dict[str, float]): result = calc_ptf_pidgeon1972_fc(**inputs) - assert result == pytest.approx(expected['field_capacity'], rel=rtol, abs=atol) + assert_close( + result, + expected['field_capacity'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) def test_calc_ptf_pidgeon1972_fc_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case(CASES_CALC_PTF_PIDGEON1972_FC) + inputs, expected, _out = prepare_vector_case(CASES_CALC_PTF_PIDGEON1972_FC) result = calc_ptf_pidgeon1972_fc(**inputs, out=None) - assert result[0] == pytest.approx(expected['field_capacity'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['field_capacity'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) def test_calc_ptf_pidgeon1972_fc_out(): - inputs, expected, rtol, atol, out = prepare_vector_case(CASES_CALC_PTF_PIDGEON1972_FC) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_PIDGEON1972_FC) result = calc_ptf_pidgeon1972_fc(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['field_capacity'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['field_capacity'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) CASES_CALC_PTF_PIDGEON1972_FC_SAND = [ - ({'sand': 50.0}, {'field_capacity': 23.66}, 1e-12, 1e-12), + ({'sand': 50.0}, {'field_capacity': 23.66}), +] +CASES_CALC_PTF_PIDGEON1972_FC_SAND_IDS = [ + 'regression_case', ] -@pytest.mark.parametrize(('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_PIDGEON1972_FC_SAND) -def test_calc_ptf_pidgeon1972_fc_sand_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +@pytest.mark.parametrize( + ('inputs', 'expected'), + CASES_CALC_PTF_PIDGEON1972_FC_SAND, + ids=CASES_CALC_PTF_PIDGEON1972_FC_SAND_IDS, +) +def test_calc_ptf_pidgeon1972_fc_sand_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_pidgeon1972_fc_sand(**inputs) - assert result == pytest.approx(expected['field_capacity'], rel=rtol, abs=atol) + assert_close( + result, + expected['field_capacity'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) def test_calc_ptf_pidgeon1972_fc_sand_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case(CASES_CALC_PTF_PIDGEON1972_FC_SAND) + inputs, expected, _out = prepare_vector_case(CASES_CALC_PTF_PIDGEON1972_FC_SAND) result = calc_ptf_pidgeon1972_fc_sand(**inputs, out=None) - assert result[0] == pytest.approx(expected['field_capacity'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['field_capacity'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) def test_calc_ptf_pidgeon1972_fc_sand_out(): - inputs, expected, rtol, atol, out = prepare_vector_case(CASES_CALC_PTF_PIDGEON1972_FC_SAND) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_PIDGEON1972_FC_SAND) result = calc_ptf_pidgeon1972_fc_sand(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['field_capacity'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['field_capacity'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) CASES_CALC_PTF_PIDGEON1972_FC_SAND_ORGANIC_MATTER = [ - ({'organic_matter': 2.0, 'sand': 50.0}, {'field_capacity': 23.27}, 1e-12, 1e-12), + ({'organic_matter': 2.0, 'sand': 50.0}, {'field_capacity': 23.27}), +] +CASES_CALC_PTF_PIDGEON1972_FC_SAND_ORGANIC_MATTER_IDS = [ + 'regression_case', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_PIDGEON1972_FC_SAND_ORGANIC_MATTER + ('inputs', 'expected'), + CASES_CALC_PTF_PIDGEON1972_FC_SAND_ORGANIC_MATTER, + ids=CASES_CALC_PTF_PIDGEON1972_FC_SAND_ORGANIC_MATTER_IDS, ) -def test_calc_ptf_pidgeon1972_fc_sand_organic_matter_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_pidgeon1972_fc_sand_organic_matter_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_pidgeon1972_fc_sand_organic_matter(**inputs) - assert result == pytest.approx(expected['field_capacity'], rel=rtol, abs=atol) + assert_close( + result, + expected['field_capacity'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) def test_calc_ptf_pidgeon1972_fc_sand_organic_matter_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( - CASES_CALC_PTF_PIDGEON1972_FC_SAND_ORGANIC_MATTER - ) + inputs, expected, _out = prepare_vector_case(CASES_CALC_PTF_PIDGEON1972_FC_SAND_ORGANIC_MATTER) result = calc_ptf_pidgeon1972_fc_sand_organic_matter(**inputs, out=None) - assert result[0] == pytest.approx(expected['field_capacity'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['field_capacity'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) def test_calc_ptf_pidgeon1972_fc_sand_organic_matter_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( - CASES_CALC_PTF_PIDGEON1972_FC_SAND_ORGANIC_MATTER - ) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_PIDGEON1972_FC_SAND_ORGANIC_MATTER) result = calc_ptf_pidgeon1972_fc_sand_organic_matter(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['field_capacity'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['field_capacity'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) CASES_CALC_PTF_PIDGEON1972_FC_VOL_SAND_ORGANIC_MATTER = [ - ({'organic_matter': 2.0, 'sand': 50.0}, {'field_capacity': 31.19}, 1e-12, 1e-12), + ({'organic_matter': 2.0, 'sand': 50.0}, {'field_capacity': 31.19}), +] +CASES_CALC_PTF_PIDGEON1972_FC_VOL_SAND_ORGANIC_MATTER_IDS = [ + 'regression_case', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_PIDGEON1972_FC_VOL_SAND_ORGANIC_MATTER + ('inputs', 'expected'), + CASES_CALC_PTF_PIDGEON1972_FC_VOL_SAND_ORGANIC_MATTER, + ids=CASES_CALC_PTF_PIDGEON1972_FC_VOL_SAND_ORGANIC_MATTER_IDS, ) -def test_calc_ptf_pidgeon1972_fc_vol_sand_organic_matter_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_pidgeon1972_fc_vol_sand_organic_matter_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_pidgeon1972_fc_vol_sand_organic_matter(**inputs) - assert result == pytest.approx(expected['field_capacity'], rel=rtol, abs=atol) + assert_close( + result, + expected['field_capacity'], + absolute=0.1, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_percent', + source='registry', + ) def test_calc_ptf_pidgeon1972_fc_vol_sand_organic_matter_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_PIDGEON1972_FC_VOL_SAND_ORGANIC_MATTER ) result = calc_ptf_pidgeon1972_fc_vol_sand_organic_matter(**inputs, out=None) - assert result[0] == pytest.approx(expected['field_capacity'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['field_capacity'], + absolute=0.1, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_percent', + source='registry', + ) def test_calc_ptf_pidgeon1972_fc_vol_sand_organic_matter_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_PIDGEON1972_FC_VOL_SAND_ORGANIC_MATTER ) result = calc_ptf_pidgeon1972_fc_vol_sand_organic_matter(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['field_capacity'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['field_capacity'], + absolute=0.1, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_percent', + source='registry', + ) CASES_CALC_PTF_PIDGEON1972_PWP = [ - ( - {'clay': 20.0, 'organic_matter': 2.0, 'silt': 30.0}, - {'permanent_wilting_point': 11.11}, - 1e-12, - 1e-12, - ), + ({'clay': 20.0, 'organic_matter': 2.0, 'silt': 30.0}, {'permanent_wilting_point': 11.11}), +] +CASES_CALC_PTF_PIDGEON1972_PWP_IDS = [ + 'regression_case', ] -@pytest.mark.parametrize(('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_PIDGEON1972_PWP) -def test_calc_ptf_pidgeon1972_pwp_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +@pytest.mark.parametrize( + ('inputs', 'expected'), CASES_CALC_PTF_PIDGEON1972_PWP, ids=CASES_CALC_PTF_PIDGEON1972_PWP_IDS +) +def test_calc_ptf_pidgeon1972_pwp_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_pidgeon1972_pwp(**inputs) - assert result == pytest.approx(expected['permanent_wilting_point'], rel=rtol, abs=atol) + assert_close( + result, + expected['permanent_wilting_point'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) def test_calc_ptf_pidgeon1972_pwp_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case(CASES_CALC_PTF_PIDGEON1972_PWP) + inputs, expected, _out = prepare_vector_case(CASES_CALC_PTF_PIDGEON1972_PWP) result = calc_ptf_pidgeon1972_pwp(**inputs, out=None) - assert result[0] == pytest.approx(expected['permanent_wilting_point'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['permanent_wilting_point'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) def test_calc_ptf_pidgeon1972_pwp_out(): - inputs, expected, rtol, atol, out = prepare_vector_case(CASES_CALC_PTF_PIDGEON1972_PWP) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_PIDGEON1972_PWP) result = calc_ptf_pidgeon1972_pwp(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['permanent_wilting_point'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['permanent_wilting_point'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) CASES_CALC_PTF_PIDGEON1972_PWP_SAND = [ - ({'sand': 50.0}, {'permanent_wilting_point': 13.91}, 1e-12, 1e-12), + ({'sand': 50.0}, {'permanent_wilting_point': 13.91}), +] +CASES_CALC_PTF_PIDGEON1972_PWP_SAND_IDS = [ + 'regression_case', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_PIDGEON1972_PWP_SAND + ('inputs', 'expected'), + CASES_CALC_PTF_PIDGEON1972_PWP_SAND, + ids=CASES_CALC_PTF_PIDGEON1972_PWP_SAND_IDS, ) -def test_calc_ptf_pidgeon1972_pwp_sand_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_pidgeon1972_pwp_sand_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_pidgeon1972_pwp_sand(**inputs) - assert result == pytest.approx(expected['permanent_wilting_point'], rel=rtol, abs=atol) + assert_close( + result, + expected['permanent_wilting_point'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) def test_calc_ptf_pidgeon1972_pwp_sand_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case(CASES_CALC_PTF_PIDGEON1972_PWP_SAND) + inputs, expected, _out = prepare_vector_case(CASES_CALC_PTF_PIDGEON1972_PWP_SAND) result = calc_ptf_pidgeon1972_pwp_sand(**inputs, out=None) - assert result[0] == pytest.approx(expected['permanent_wilting_point'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['permanent_wilting_point'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) def test_calc_ptf_pidgeon1972_pwp_sand_out(): - inputs, expected, rtol, atol, out = prepare_vector_case(CASES_CALC_PTF_PIDGEON1972_PWP_SAND) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_PIDGEON1972_PWP_SAND) result = calc_ptf_pidgeon1972_pwp_sand(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['permanent_wilting_point'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['permanent_wilting_point'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) CASES_CALC_PTF_PIDGEON1972_PWP_SAND_ORGANIC_MATTER = [ - ({'organic_matter': 2.0, 'sand': 50.0}, {'permanent_wilting_point': 15.28}, 1e-12, 1e-12), + ({'organic_matter': 2.0, 'sand': 50.0}, {'permanent_wilting_point': 15.28}), +] +CASES_CALC_PTF_PIDGEON1972_PWP_SAND_ORGANIC_MATTER_IDS = [ + 'regression_case', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_PIDGEON1972_PWP_SAND_ORGANIC_MATTER + ('inputs', 'expected'), + CASES_CALC_PTF_PIDGEON1972_PWP_SAND_ORGANIC_MATTER, + ids=CASES_CALC_PTF_PIDGEON1972_PWP_SAND_ORGANIC_MATTER_IDS, ) -def test_calc_ptf_pidgeon1972_pwp_sand_organic_matter_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_pidgeon1972_pwp_sand_organic_matter_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_pidgeon1972_pwp_sand_organic_matter(**inputs) - assert result == pytest.approx(expected['permanent_wilting_point'], rel=rtol, abs=atol) + assert_close( + result, + expected['permanent_wilting_point'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) def test_calc_ptf_pidgeon1972_pwp_sand_organic_matter_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( - CASES_CALC_PTF_PIDGEON1972_PWP_SAND_ORGANIC_MATTER - ) + inputs, expected, _out = prepare_vector_case(CASES_CALC_PTF_PIDGEON1972_PWP_SAND_ORGANIC_MATTER) result = calc_ptf_pidgeon1972_pwp_sand_organic_matter(**inputs, out=None) - assert result[0] == pytest.approx(expected['permanent_wilting_point'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['permanent_wilting_point'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) def test_calc_ptf_pidgeon1972_pwp_sand_organic_matter_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( - CASES_CALC_PTF_PIDGEON1972_PWP_SAND_ORGANIC_MATTER - ) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_PIDGEON1972_PWP_SAND_ORGANIC_MATTER) result = calc_ptf_pidgeon1972_pwp_sand_organic_matter(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['permanent_wilting_point'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['permanent_wilting_point'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) CASES_CALC_PTF_PIDGEON1972_AWC = [ - ({'clay': 20.0, 'organic_matter': 2.0}, {'available_water_capacity': 151.48}, 1e-12, 1e-12), + ({'clay': 20.0, 'organic_matter': 2.0}, {'available_water_capacity': 151.48}), +] +CASES_CALC_PTF_PIDGEON1972_AWC_IDS = [ + 'regression_case', ] -@pytest.mark.parametrize(('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_PIDGEON1972_AWC) -def test_calc_ptf_pidgeon1972_awc_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +@pytest.mark.parametrize( + ('inputs', 'expected'), CASES_CALC_PTF_PIDGEON1972_AWC, ids=CASES_CALC_PTF_PIDGEON1972_AWC_IDS +) +def test_calc_ptf_pidgeon1972_awc_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_pidgeon1972_awc(**inputs) - assert result == pytest.approx(expected['available_water_capacity'], rel=rtol, abs=atol) + assert_close( + result, + expected['available_water_capacity'], + absolute=0.1, + relative=0.0, + quantity='available_water_capacity', + unit='millimeter_per_meter', + source='registry', + ) def test_calc_ptf_pidgeon1972_awc_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case(CASES_CALC_PTF_PIDGEON1972_AWC) + inputs, expected, _out = prepare_vector_case(CASES_CALC_PTF_PIDGEON1972_AWC) result = calc_ptf_pidgeon1972_awc(**inputs, out=None) - assert result[0] == pytest.approx(expected['available_water_capacity'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['available_water_capacity'], + absolute=0.1, + relative=0.0, + quantity='available_water_capacity', + unit='millimeter_per_meter', + source='registry', + ) def test_calc_ptf_pidgeon1972_awc_out(): - inputs, expected, rtol, atol, out = prepare_vector_case(CASES_CALC_PTF_PIDGEON1972_AWC) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_PIDGEON1972_AWC) result = calc_ptf_pidgeon1972_awc(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['available_water_capacity'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['available_water_capacity'], + absolute=0.1, + relative=0.0, + quantity='available_water_capacity', + unit='millimeter_per_meter', + source='registry', + ) CASES_CALC_PTF_PIDGEON1972_AWC_SAND_ORGANIC_MATTER = [ - ({'organic_matter': 2.0, 'sand': 50.0}, {'available_water_capacity': 109.24}, 1e-12, 1e-12), + ({'organic_matter': 2.0, 'sand': 50.0}, {'available_water_capacity': 109.24}), +] +CASES_CALC_PTF_PIDGEON1972_AWC_SAND_ORGANIC_MATTER_IDS = [ + 'regression_case', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_PIDGEON1972_AWC_SAND_ORGANIC_MATTER + ('inputs', 'expected'), + CASES_CALC_PTF_PIDGEON1972_AWC_SAND_ORGANIC_MATTER, + ids=CASES_CALC_PTF_PIDGEON1972_AWC_SAND_ORGANIC_MATTER_IDS, ) -def test_calc_ptf_pidgeon1972_awc_sand_organic_matter_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_pidgeon1972_awc_sand_organic_matter_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_pidgeon1972_awc_sand_organic_matter(**inputs) - assert result == pytest.approx(expected['available_water_capacity'], rel=rtol, abs=atol) + assert_close( + result, + expected['available_water_capacity'], + absolute=0.1, + relative=0.0, + quantity='available_water_capacity', + unit='millimeter_per_meter', + source='registry', + ) def test_calc_ptf_pidgeon1972_awc_sand_organic_matter_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( - CASES_CALC_PTF_PIDGEON1972_AWC_SAND_ORGANIC_MATTER - ) + inputs, expected, _out = prepare_vector_case(CASES_CALC_PTF_PIDGEON1972_AWC_SAND_ORGANIC_MATTER) result = calc_ptf_pidgeon1972_awc_sand_organic_matter(**inputs, out=None) - assert result[0] == pytest.approx(expected['available_water_capacity'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['available_water_capacity'], + absolute=0.1, + relative=0.0, + quantity='available_water_capacity', + unit='millimeter_per_meter', + source='registry', + ) def test_calc_ptf_pidgeon1972_awc_sand_organic_matter_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( - CASES_CALC_PTF_PIDGEON1972_AWC_SAND_ORGANIC_MATTER - ) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_PIDGEON1972_AWC_SAND_ORGANIC_MATTER) result = calc_ptf_pidgeon1972_awc_sand_organic_matter(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['available_water_capacity'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['available_water_capacity'], + absolute=0.1, + relative=0.0, + quantity='available_water_capacity', + unit='millimeter_per_meter', + source='registry', + ) CASES_CALC_PTF_PIDGEON1972_AWC_COARSE_SAND = [ - ({'coarse_sand': 20.0}, {'available_water_capacity': 115.1}, 1e-12, 1e-12), + ({'coarse_sand': 20.0}, {'available_water_capacity': 115.1}), +] +CASES_CALC_PTF_PIDGEON1972_AWC_COARSE_SAND_IDS = [ + 'regression_case', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_PIDGEON1972_AWC_COARSE_SAND + ('inputs', 'expected'), + CASES_CALC_PTF_PIDGEON1972_AWC_COARSE_SAND, + ids=CASES_CALC_PTF_PIDGEON1972_AWC_COARSE_SAND_IDS, ) -def test_calc_ptf_pidgeon1972_awc_coarse_sand_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_pidgeon1972_awc_coarse_sand_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_pidgeon1972_awc_coarse_sand(**inputs) - assert result == pytest.approx(expected['available_water_capacity'], rel=rtol, abs=atol) + assert_close( + result, + expected['available_water_capacity'], + absolute=0.1, + relative=0.0, + quantity='available_water_capacity', + unit='millimeter_per_meter', + source='registry', + ) def test_calc_ptf_pidgeon1972_awc_coarse_sand_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( - CASES_CALC_PTF_PIDGEON1972_AWC_COARSE_SAND - ) + inputs, expected, _out = prepare_vector_case(CASES_CALC_PTF_PIDGEON1972_AWC_COARSE_SAND) result = calc_ptf_pidgeon1972_awc_coarse_sand(**inputs, out=None) - assert result[0] == pytest.approx(expected['available_water_capacity'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['available_water_capacity'], + absolute=0.1, + relative=0.0, + quantity='available_water_capacity', + unit='millimeter_per_meter', + source='registry', + ) def test_calc_ptf_pidgeon1972_awc_coarse_sand_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( - CASES_CALC_PTF_PIDGEON1972_AWC_COARSE_SAND - ) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_PIDGEON1972_AWC_COARSE_SAND) result = calc_ptf_pidgeon1972_awc_coarse_sand(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['available_water_capacity'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['available_water_capacity'], + absolute=0.1, + relative=0.0, + quantity='available_water_capacity', + unit='millimeter_per_meter', + source='registry', + ) CASES_CALC_PTF_PIDGEON1972_AWC_FINE_SAND = [ - ({'fine_sand': 20.0}, {'available_water_capacity': 119.9}, 1e-12, 1e-12), + ({'fine_sand': 20.0}, {'available_water_capacity': 119.9}), +] +CASES_CALC_PTF_PIDGEON1972_AWC_FINE_SAND_IDS = [ + 'regression_case', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_PIDGEON1972_AWC_FINE_SAND + ('inputs', 'expected'), + CASES_CALC_PTF_PIDGEON1972_AWC_FINE_SAND, + ids=CASES_CALC_PTF_PIDGEON1972_AWC_FINE_SAND_IDS, ) -def test_calc_ptf_pidgeon1972_awc_fine_sand_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_pidgeon1972_awc_fine_sand_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_pidgeon1972_awc_fine_sand(**inputs) - assert result == pytest.approx(expected['available_water_capacity'], rel=rtol, abs=atol) + assert_close( + result, + expected['available_water_capacity'], + absolute=0.1, + relative=0.0, + quantity='available_water_capacity', + unit='millimeter_per_meter', + source='registry', + ) def test_calc_ptf_pidgeon1972_awc_fine_sand_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( - CASES_CALC_PTF_PIDGEON1972_AWC_FINE_SAND - ) + inputs, expected, _out = prepare_vector_case(CASES_CALC_PTF_PIDGEON1972_AWC_FINE_SAND) result = calc_ptf_pidgeon1972_awc_fine_sand(**inputs, out=None) - assert result[0] == pytest.approx(expected['available_water_capacity'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['available_water_capacity'], + absolute=0.1, + relative=0.0, + quantity='available_water_capacity', + unit='millimeter_per_meter', + source='registry', + ) def test_calc_ptf_pidgeon1972_awc_fine_sand_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( - CASES_CALC_PTF_PIDGEON1972_AWC_FINE_SAND - ) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_PIDGEON1972_AWC_FINE_SAND) result = calc_ptf_pidgeon1972_awc_fine_sand(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['available_water_capacity'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['available_water_capacity'], + absolute=0.1, + relative=0.0, + quantity='available_water_capacity', + unit='millimeter_per_meter', + source='registry', + ) CASES_CALC_PTF_PIDGEON1972_AWC_VERY_FINE_SAND = [ - ({'very_fine_sand': 10.0}, {'available_water_capacity': 112.7}, 1e-12, 1e-12), + ({'very_fine_sand': 10.0}, {'available_water_capacity': 112.7}), +] +CASES_CALC_PTF_PIDGEON1972_AWC_VERY_FINE_SAND_IDS = [ + 'regression_case', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_PIDGEON1972_AWC_VERY_FINE_SAND + ('inputs', 'expected'), + CASES_CALC_PTF_PIDGEON1972_AWC_VERY_FINE_SAND, + ids=CASES_CALC_PTF_PIDGEON1972_AWC_VERY_FINE_SAND_IDS, ) -def test_calc_ptf_pidgeon1972_awc_very_fine_sand_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_pidgeon1972_awc_very_fine_sand_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_pidgeon1972_awc_very_fine_sand(**inputs) - assert result == pytest.approx(expected['available_water_capacity'], rel=rtol, abs=atol) + assert_close( + result, + expected['available_water_capacity'], + absolute=0.1, + relative=0.0, + quantity='available_water_capacity', + unit='millimeter_per_meter', + source='registry', + ) def test_calc_ptf_pidgeon1972_awc_very_fine_sand_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( - CASES_CALC_PTF_PIDGEON1972_AWC_VERY_FINE_SAND - ) + inputs, expected, _out = prepare_vector_case(CASES_CALC_PTF_PIDGEON1972_AWC_VERY_FINE_SAND) result = calc_ptf_pidgeon1972_awc_very_fine_sand(**inputs, out=None) - assert result[0] == pytest.approx(expected['available_water_capacity'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['available_water_capacity'], + absolute=0.1, + relative=0.0, + quantity='available_water_capacity', + unit='millimeter_per_meter', + source='registry', + ) def test_calc_ptf_pidgeon1972_awc_very_fine_sand_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( - CASES_CALC_PTF_PIDGEON1972_AWC_VERY_FINE_SAND - ) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_PIDGEON1972_AWC_VERY_FINE_SAND) result = calc_ptf_pidgeon1972_awc_very_fine_sand(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['available_water_capacity'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['available_water_capacity'], + absolute=0.1, + relative=0.0, + quantity='available_water_capacity', + unit='millimeter_per_meter', + source='registry', + ) CASES_CALC_PTF_PIDGEON1972_EAWC = [ ( {'clay': 20.0, 'organic_matter': 2.0, 'silt': 30.0}, {'extended_available_water_capacity': 16.12}, - 1e-12, - 1e-12, ), ] +CASES_CALC_PTF_PIDGEON1972_EAWC_IDS = [ + 'regression_case', +] -@pytest.mark.parametrize(('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_PIDGEON1972_EAWC) -def test_calc_ptf_pidgeon1972_eawc_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +@pytest.mark.parametrize( + ('inputs', 'expected'), CASES_CALC_PTF_PIDGEON1972_EAWC, ids=CASES_CALC_PTF_PIDGEON1972_EAWC_IDS +) +def test_calc_ptf_pidgeon1972_eawc_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_pidgeon1972_eawc(**inputs) - assert result == pytest.approx( - expected['extended_available_water_capacity'], rel=rtol, abs=atol + assert_close( + result, + expected['extended_available_water_capacity'], + absolute=0.1, + relative=0.0, + quantity='available_water_capacity', + unit='millimeter_per_meter', + source='registry', ) def test_calc_ptf_pidgeon1972_eawc_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case(CASES_CALC_PTF_PIDGEON1972_EAWC) + inputs, expected, _out = prepare_vector_case(CASES_CALC_PTF_PIDGEON1972_EAWC) result = calc_ptf_pidgeon1972_eawc(**inputs, out=None) - assert result[0] == pytest.approx( - expected['extended_available_water_capacity'], rel=rtol, abs=atol + assert_close( + result[0], + expected['extended_available_water_capacity'], + absolute=0.1, + relative=0.0, + quantity='available_water_capacity', + unit='millimeter_per_meter', + source='registry', ) def test_calc_ptf_pidgeon1972_eawc_out(): - inputs, expected, rtol, atol, out = prepare_vector_case(CASES_CALC_PTF_PIDGEON1972_EAWC) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_PIDGEON1972_EAWC) result = calc_ptf_pidgeon1972_eawc(**inputs, out=out) assert result is out - assert result[0] == pytest.approx( - expected['extended_available_water_capacity'], rel=rtol, abs=atol + assert_close( + result[0], + expected['extended_available_water_capacity'], + absolute=0.1, + relative=0.0, + quantity='available_water_capacity', + unit='millimeter_per_meter', + source='registry', ) CASES_CALC_PTF_PIDGEON1972_EAWC_SAND = [ - ({'sand': 50.0}, {'extended_available_water_capacity': 51.7}, 1e-12, 1e-12), + ({'sand': 50.0}, {'extended_available_water_capacity': 51.7}), +] +CASES_CALC_PTF_PIDGEON1972_EAWC_SAND_IDS = [ + 'regression_case', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_PIDGEON1972_EAWC_SAND + ('inputs', 'expected'), + CASES_CALC_PTF_PIDGEON1972_EAWC_SAND, + ids=CASES_CALC_PTF_PIDGEON1972_EAWC_SAND_IDS, ) -def test_calc_ptf_pidgeon1972_eawc_sand_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_pidgeon1972_eawc_sand_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_pidgeon1972_eawc_sand(**inputs) - assert result == pytest.approx( - expected['extended_available_water_capacity'], rel=rtol, abs=atol + assert_close( + result, + expected['extended_available_water_capacity'], + absolute=0.1, + relative=0.0, + quantity='available_water_capacity', + unit='millimeter_per_meter', + source='registry', ) def test_calc_ptf_pidgeon1972_eawc_sand_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case(CASES_CALC_PTF_PIDGEON1972_EAWC_SAND) + inputs, expected, _out = prepare_vector_case(CASES_CALC_PTF_PIDGEON1972_EAWC_SAND) result = calc_ptf_pidgeon1972_eawc_sand(**inputs, out=None) - assert result[0] == pytest.approx( - expected['extended_available_water_capacity'], rel=rtol, abs=atol + assert_close( + result[0], + expected['extended_available_water_capacity'], + absolute=0.1, + relative=0.0, + quantity='available_water_capacity', + unit='millimeter_per_meter', + source='registry', ) def test_calc_ptf_pidgeon1972_eawc_sand_out(): - inputs, expected, rtol, atol, out = prepare_vector_case(CASES_CALC_PTF_PIDGEON1972_EAWC_SAND) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_PIDGEON1972_EAWC_SAND) result = calc_ptf_pidgeon1972_eawc_sand(**inputs, out=out) assert result is out - assert result[0] == pytest.approx( - expected['extended_available_water_capacity'], rel=rtol, abs=atol + assert_close( + result[0], + expected['extended_available_water_capacity'], + absolute=0.1, + relative=0.0, + quantity='available_water_capacity', + unit='millimeter_per_meter', + source='registry', ) CASES_CALC_PTF_PIDGEON1972_EAWC_SAND_ORGANIC_MATTER = [ - ( - {'organic_matter': 2.0, 'sand': 50.0}, - {'extended_available_water_capacity': 56.26}, - 1e-12, - 1e-12, - ), + ({'organic_matter': 2.0, 'sand': 50.0}, {'extended_available_water_capacity': 56.26}), +] +CASES_CALC_PTF_PIDGEON1972_EAWC_SAND_ORGANIC_MATTER_IDS = [ + 'regression_case', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_PIDGEON1972_EAWC_SAND_ORGANIC_MATTER + ('inputs', 'expected'), + CASES_CALC_PTF_PIDGEON1972_EAWC_SAND_ORGANIC_MATTER, + ids=CASES_CALC_PTF_PIDGEON1972_EAWC_SAND_ORGANIC_MATTER_IDS, ) -def test_calc_ptf_pidgeon1972_eawc_sand_organic_matter_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_pidgeon1972_eawc_sand_organic_matter_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_pidgeon1972_eawc_sand_organic_matter(**inputs) - assert result == pytest.approx( - expected['extended_available_water_capacity'], rel=rtol, abs=atol + assert_close( + result, + expected['extended_available_water_capacity'], + absolute=0.1, + relative=0.0, + quantity='available_water_capacity', + unit='millimeter_per_meter', + source='registry', ) def test_calc_ptf_pidgeon1972_eawc_sand_organic_matter_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_PIDGEON1972_EAWC_SAND_ORGANIC_MATTER ) result = calc_ptf_pidgeon1972_eawc_sand_organic_matter(**inputs, out=None) - assert result[0] == pytest.approx( - expected['extended_available_water_capacity'], rel=rtol, abs=atol + assert_close( + result[0], + expected['extended_available_water_capacity'], + absolute=0.1, + relative=0.0, + quantity='available_water_capacity', + unit='millimeter_per_meter', + source='registry', ) def test_calc_ptf_pidgeon1972_eawc_sand_organic_matter_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( - CASES_CALC_PTF_PIDGEON1972_EAWC_SAND_ORGANIC_MATTER - ) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_PIDGEON1972_EAWC_SAND_ORGANIC_MATTER) result = calc_ptf_pidgeon1972_eawc_sand_organic_matter(**inputs, out=out) assert result is out - assert result[0] == pytest.approx( - expected['extended_available_water_capacity'], rel=rtol, abs=atol + assert_close( + result[0], + expected['extended_available_water_capacity'], + absolute=0.1, + relative=0.0, + quantity='available_water_capacity', + unit='millimeter_per_meter', + source='registry', ) CASES_CALC_PTF_PIDGEON1972_EAWC_COARSE_SAND_ORGANIC_MATTER = [ - ( - {'coarse_sand': 20.0, 'organic_matter': 2.0}, - {'extended_available_water_capacity': 53.72}, - 1e-12, - 1e-12, - ), + ({'coarse_sand': 20.0, 'organic_matter': 2.0}, {'extended_available_water_capacity': 53.72}), +] +CASES_CALC_PTF_PIDGEON1972_EAWC_COARSE_SAND_ORGANIC_MATTER_IDS = [ + 'regression_case', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), + ('inputs', 'expected'), CASES_CALC_PTF_PIDGEON1972_EAWC_COARSE_SAND_ORGANIC_MATTER, + ids=CASES_CALC_PTF_PIDGEON1972_EAWC_COARSE_SAND_ORGANIC_MATTER_IDS, ) -def test_calc_ptf_pidgeon1972_eawc_coarse_sand_organic_matter_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_pidgeon1972_eawc_coarse_sand_organic_matter_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_pidgeon1972_eawc_coarse_sand_organic_matter(**inputs) - assert result == pytest.approx( - expected['extended_available_water_capacity'], rel=rtol, abs=atol + assert_close( + result, + expected['extended_available_water_capacity'], + absolute=0.1, + relative=0.0, + quantity='available_water_capacity', + unit='millimeter_per_meter', + source='registry', ) def test_calc_ptf_pidgeon1972_eawc_coarse_sand_organic_matter_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_PIDGEON1972_EAWC_COARSE_SAND_ORGANIC_MATTER ) result = calc_ptf_pidgeon1972_eawc_coarse_sand_organic_matter(**inputs, out=None) - assert result[0] == pytest.approx( - expected['extended_available_water_capacity'], rel=rtol, abs=atol + assert_close( + result[0], + expected['extended_available_water_capacity'], + absolute=0.1, + relative=0.0, + quantity='available_water_capacity', + unit='millimeter_per_meter', + source='registry', ) def test_calc_ptf_pidgeon1972_eawc_coarse_sand_organic_matter_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_PIDGEON1972_EAWC_COARSE_SAND_ORGANIC_MATTER ) result = calc_ptf_pidgeon1972_eawc_coarse_sand_organic_matter(**inputs, out=out) assert result is out - assert result[0] == pytest.approx( - expected['extended_available_water_capacity'], rel=rtol, abs=atol + assert_close( + result[0], + expected['extended_available_water_capacity'], + absolute=0.1, + relative=0.0, + quantity='available_water_capacity', + unit='millimeter_per_meter', + source='registry', ) CASES_CALC_PTF_PIDGEON1972_EAWC_FINE_SAND_ORGANIC_MATTER = [ - ( - {'fine_sand': 20.0, 'organic_matter': 2.0}, - {'extended_available_water_capacity': 59.58}, - 1e-12, - 1e-12, - ), + ({'fine_sand': 20.0, 'organic_matter': 2.0}, {'extended_available_water_capacity': 59.58}), +] +CASES_CALC_PTF_PIDGEON1972_EAWC_FINE_SAND_ORGANIC_MATTER_IDS = [ + 'regression_case', ] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_PIDGEON1972_EAWC_FINE_SAND_ORGANIC_MATTER + ('inputs', 'expected'), + CASES_CALC_PTF_PIDGEON1972_EAWC_FINE_SAND_ORGANIC_MATTER, + ids=CASES_CALC_PTF_PIDGEON1972_EAWC_FINE_SAND_ORGANIC_MATTER_IDS, ) -def test_calc_ptf_pidgeon1972_eawc_fine_sand_organic_matter_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_pidgeon1972_eawc_fine_sand_organic_matter_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_pidgeon1972_eawc_fine_sand_organic_matter(**inputs) - assert result == pytest.approx( - expected['extended_available_water_capacity'], rel=rtol, abs=atol + assert_close( + result, + expected['extended_available_water_capacity'], + absolute=0.1, + relative=0.0, + quantity='available_water_capacity', + unit='millimeter_per_meter', + source='registry', ) def test_calc_ptf_pidgeon1972_eawc_fine_sand_organic_matter_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_PIDGEON1972_EAWC_FINE_SAND_ORGANIC_MATTER ) result = calc_ptf_pidgeon1972_eawc_fine_sand_organic_matter(**inputs, out=None) - assert result[0] == pytest.approx( - expected['extended_available_water_capacity'], rel=rtol, abs=atol + assert_close( + result[0], + expected['extended_available_water_capacity'], + absolute=0.1, + relative=0.0, + quantity='available_water_capacity', + unit='millimeter_per_meter', + source='registry', ) def test_calc_ptf_pidgeon1972_eawc_fine_sand_organic_matter_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_PIDGEON1972_EAWC_FINE_SAND_ORGANIC_MATTER ) result = calc_ptf_pidgeon1972_eawc_fine_sand_organic_matter(**inputs, out=out) assert result is out - assert result[0] == pytest.approx( - expected['extended_available_water_capacity'], rel=rtol, abs=atol + assert_close( + result[0], + expected['extended_available_water_capacity'], + absolute=0.1, + relative=0.0, + quantity='available_water_capacity', + unit='millimeter_per_meter', + source='registry', ) diff --git a/targets/ptfkit-py/tests/test_puckett1985.py b/targets/ptfkit-py/tests/test_puckett1985.py index 021a987..abe6a3d 100644 --- a/targets/ptfkit-py/tests/test_puckett1985.py +++ b/targets/ptfkit-py/tests/test_puckett1985.py @@ -3,7 +3,7 @@ import pytest -from _helpers import prepare_vector_case +from _helpers import assert_close, prepare_vector_case from ptfkit.puckett1985 import Puckett1985PTFResult, calc_ptf_puckett1985 @@ -11,76 +11,337 @@ ( {'bulk_density': 1.67, 'clay': 11.8, 'fine_sand': 36.4, 'porosity': 0.38, 'sand': 70.9}, { - 'k_sat': 4.2399741e-6, 'theta_0': 0.34288, 'theta_1': 0.33926, + 'theta_5': 0.3938615, 'theta_10': 0.39330438, + 'theta_30': 0.34432936, + 'theta_60': 0.31153562, 'theta_100': 0.29292896, + 'theta_500': 0.2513588, 'theta_1000': 0.25187788, 'theta_1500': 0.22746346, - 'theta_30': 0.34432936, - 'theta_5': 0.3938615, - 'theta_500': 0.2513588, - 'theta_60': 0.31153562, + 'k_sat': 4.2399741e-6, }, - 1e-8, - 1e-12, ), ] +CASES_CALC_PTF_PUCKETT1985_IDS = [ + 'cahaba_ap', +] -@pytest.mark.parametrize(('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_PUCKETT1985) -def test_calc_ptf_puckett1985_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float -): +@pytest.mark.parametrize( + ('inputs', 'expected'), CASES_CALC_PTF_PUCKETT1985, ids=CASES_CALC_PTF_PUCKETT1985_IDS +) +def test_calc_ptf_puckett1985_verification(inputs: dict[str, float], expected: dict[str, float]): result = calc_ptf_puckett1985(**inputs) - assert result.theta_0 == pytest.approx(expected['theta_0'], rel=rtol, abs=atol) - assert result.theta_1 == pytest.approx(expected['theta_1'], rel=rtol, abs=atol) - assert result.theta_5 == pytest.approx(expected['theta_5'], rel=rtol, abs=atol) - assert result.theta_10 == pytest.approx(expected['theta_10'], rel=rtol, abs=atol) - assert result.theta_30 == pytest.approx(expected['theta_30'], rel=rtol, abs=atol) - assert result.theta_60 == pytest.approx(expected['theta_60'], rel=rtol, abs=atol) - assert result.theta_100 == pytest.approx(expected['theta_100'], rel=rtol, abs=atol) - assert result.theta_500 == pytest.approx(expected['theta_500'], rel=rtol, abs=atol) - assert result.theta_1000 == pytest.approx(expected['theta_1000'], rel=rtol, abs=atol) - assert result.theta_1500 == pytest.approx(expected['theta_1500'], rel=rtol, abs=atol) - assert result.k_sat == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result.theta_0, + expected['theta_0'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_1, + expected['theta_1'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_5, + expected['theta_5'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_10, + expected['theta_10'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_30, + expected['theta_30'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_60, + expected['theta_60'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_100, + expected['theta_100'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_500, + expected['theta_500'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_1000, + expected['theta_1000'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_1500, + expected['theta_1500'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.k_sat, + expected['k_sat'], + absolute=1e-10, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='meter_per_second', + source='registry', + ) def test_calc_ptf_puckett1985_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( - CASES_CALC_PTF_PUCKETT1985, Puckett1985PTFResult - ) + inputs, expected, _out = prepare_vector_case(CASES_CALC_PTF_PUCKETT1985, Puckett1985PTFResult) result = calc_ptf_puckett1985(**inputs, out=None) - assert result.theta_0[0] == pytest.approx(expected['theta_0'], rel=rtol, abs=atol) - assert result.theta_1[0] == pytest.approx(expected['theta_1'], rel=rtol, abs=atol) - assert result.theta_5[0] == pytest.approx(expected['theta_5'], rel=rtol, abs=atol) - assert result.theta_10[0] == pytest.approx(expected['theta_10'], rel=rtol, abs=atol) - assert result.theta_30[0] == pytest.approx(expected['theta_30'], rel=rtol, abs=atol) - assert result.theta_60[0] == pytest.approx(expected['theta_60'], rel=rtol, abs=atol) - assert result.theta_100[0] == pytest.approx(expected['theta_100'], rel=rtol, abs=atol) - assert result.theta_500[0] == pytest.approx(expected['theta_500'], rel=rtol, abs=atol) - assert result.theta_1000[0] == pytest.approx(expected['theta_1000'], rel=rtol, abs=atol) - assert result.theta_1500[0] == pytest.approx(expected['theta_1500'], rel=rtol, abs=atol) - assert result.k_sat[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result.theta_0[0], + expected['theta_0'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_1[0], + expected['theta_1'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_5[0], + expected['theta_5'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_10[0], + expected['theta_10'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_30[0], + expected['theta_30'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_60[0], + expected['theta_60'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_100[0], + expected['theta_100'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_500[0], + expected['theta_500'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_1000[0], + expected['theta_1000'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_1500[0], + expected['theta_1500'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.k_sat[0], + expected['k_sat'], + absolute=1e-10, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='meter_per_second', + source='registry', + ) def test_calc_ptf_puckett1985_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( - CASES_CALC_PTF_PUCKETT1985, Puckett1985PTFResult - ) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_PUCKETT1985, Puckett1985PTFResult) result = calc_ptf_puckett1985(**inputs, out=out) for actual, expected_out in zip(result, out, strict=True): assert actual is expected_out - assert result.theta_0[0] == pytest.approx(expected['theta_0'], rel=rtol, abs=atol) - assert result.theta_1[0] == pytest.approx(expected['theta_1'], rel=rtol, abs=atol) - assert result.theta_5[0] == pytest.approx(expected['theta_5'], rel=rtol, abs=atol) - assert result.theta_10[0] == pytest.approx(expected['theta_10'], rel=rtol, abs=atol) - assert result.theta_30[0] == pytest.approx(expected['theta_30'], rel=rtol, abs=atol) - assert result.theta_60[0] == pytest.approx(expected['theta_60'], rel=rtol, abs=atol) - assert result.theta_100[0] == pytest.approx(expected['theta_100'], rel=rtol, abs=atol) - assert result.theta_500[0] == pytest.approx(expected['theta_500'], rel=rtol, abs=atol) - assert result.theta_1000[0] == pytest.approx(expected['theta_1000'], rel=rtol, abs=atol) - assert result.theta_1500[0] == pytest.approx(expected['theta_1500'], rel=rtol, abs=atol) - assert result.k_sat[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result.theta_0[0], + expected['theta_0'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_1[0], + expected['theta_1'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_5[0], + expected['theta_5'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_10[0], + expected['theta_10'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_30[0], + expected['theta_30'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_60[0], + expected['theta_60'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_100[0], + expected['theta_100'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_500[0], + expected['theta_500'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_1000[0], + expected['theta_1000'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_1500[0], + expected['theta_1500'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.k_sat[0], + expected['k_sat'], + absolute=1e-10, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='meter_per_second', + source='registry', + ) diff --git a/targets/ptfkit-py/tests/test_rawls1982.py b/targets/ptfkit-py/tests/test_rawls1982.py index 82b36dd..2bdd432 100644 --- a/targets/ptfkit-py/tests/test_rawls1982.py +++ b/targets/ptfkit-py/tests/test_rawls1982.py @@ -3,7 +3,7 @@ import pytest -from _helpers import prepare_vector_case +from _helpers import assert_close, prepare_vector_case from ptfkit.rawls1982 import ( Rawls1982PTFResult, calc_full_wrc_rawls1982, @@ -13,62 +13,113 @@ CASES_CALC_THETA_1500_RAWLS1982 = [ - ({'clay': 5.12, 'organic_matter': 0.1}, {'theta_1500': 0.05318}, 1e-10, 1e-12), + ({'clay': 5.12, 'organic_matter': 0.1}, {'theta_1500': 0.05318}), +] +CASES_CALC_THETA_1500_RAWLS1982_IDS = [ + 'loamy_sand', ] -@pytest.mark.parametrize(('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_THETA_1500_RAWLS1982) -def test_calc_theta_1500_rawls1982_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +@pytest.mark.parametrize( + ('inputs', 'expected'), CASES_CALC_THETA_1500_RAWLS1982, ids=CASES_CALC_THETA_1500_RAWLS1982_IDS +) +def test_calc_theta_1500_rawls1982_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_theta_1500_rawls1982(**inputs) - assert result == pytest.approx(expected['theta_1500'], rel=rtol, abs=atol) + assert_close( + result, + expected['theta_1500'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) def test_calc_theta_1500_rawls1982_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case(CASES_CALC_THETA_1500_RAWLS1982) + inputs, expected, _out = prepare_vector_case(CASES_CALC_THETA_1500_RAWLS1982) result = calc_theta_1500_rawls1982(**inputs, out=None) - assert result[0] == pytest.approx(expected['theta_1500'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['theta_1500'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) def test_calc_theta_1500_rawls1982_out(): - inputs, expected, rtol, atol, out = prepare_vector_case(CASES_CALC_THETA_1500_RAWLS1982) + inputs, expected, out = prepare_vector_case(CASES_CALC_THETA_1500_RAWLS1982) result = calc_theta_1500_rawls1982(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['theta_1500'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['theta_1500'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) CASES_CALC_THETA_33_RAWLS1982 = [ - ( - {'organic_matter': 0.1, 'sand': 85.0, 'theta_1500': 0.05318}, - {'theta_33': 0.1179896}, - 1e-10, - 1e-12, - ), + ({'organic_matter': 0.1, 'sand': 85.0, 'theta_1500': 0.05318}, {'theta_33': 0.1179896}), +] +CASES_CALC_THETA_33_RAWLS1982_IDS = [ + 'loamy_sand_with_estimated_theta_1500', ] -@pytest.mark.parametrize(('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_THETA_33_RAWLS1982) -def test_calc_theta_33_rawls1982_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float -): +@pytest.mark.parametrize( + ('inputs', 'expected'), CASES_CALC_THETA_33_RAWLS1982, ids=CASES_CALC_THETA_33_RAWLS1982_IDS +) +def test_calc_theta_33_rawls1982_verification(inputs: dict[str, float], expected: dict[str, float]): result = calc_theta_33_rawls1982(**inputs) - assert result == pytest.approx(expected['theta_33'], rel=rtol, abs=atol) + assert_close( + result, + expected['theta_33'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) def test_calc_theta_33_rawls1982_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case(CASES_CALC_THETA_33_RAWLS1982) + inputs, expected, _out = prepare_vector_case(CASES_CALC_THETA_33_RAWLS1982) result = calc_theta_33_rawls1982(**inputs, out=None) - assert result[0] == pytest.approx(expected['theta_33'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['theta_33'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) def test_calc_theta_33_rawls1982_out(): - inputs, expected, rtol, atol, out = prepare_vector_case(CASES_CALC_THETA_33_RAWLS1982) + inputs, expected, out = prepare_vector_case(CASES_CALC_THETA_33_RAWLS1982) result = calc_theta_33_rawls1982(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['theta_33'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['theta_33'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) CASES_CALC_FULL_WRC_RAWLS1982 = [ @@ -81,80 +132,365 @@ def test_calc_theta_33_rawls1982_out(): 'theta_33': 0.091, }, { + 'theta_4': 0.247242, + 'theta_7': 0.968738, 'theta_10': 0.145588, - 'theta_100': 0.063192, - 'theta_1000': 0.038932, - 'theta_1500': 0.033, 'theta_20': 0.10483, - 'theta_200': 0.052946, 'theta_33': 0.091, - 'theta_4': 0.247242, - 'theta_400': 0.045826, 'theta_60': 0.075428, - 'theta_7': 0.968738, + 'theta_100': 0.063192, + 'theta_200': 0.052946, + 'theta_400': 0.045826, 'theta_700': 0.041824, + 'theta_1000': 0.038932, + 'theta_1500': 0.033, }, - 1e-10, - 1e-12, ), ] +CASES_CALC_FULL_WRC_RAWLS1982_IDS = [ + 'loamy_sand', +] -@pytest.mark.parametrize(('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_FULL_WRC_RAWLS1982) -def test_calc_full_wrc_rawls1982_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float -): +@pytest.mark.parametrize( + ('inputs', 'expected'), CASES_CALC_FULL_WRC_RAWLS1982, ids=CASES_CALC_FULL_WRC_RAWLS1982_IDS +) +def test_calc_full_wrc_rawls1982_verification(inputs: dict[str, float], expected: dict[str, float]): result = calc_full_wrc_rawls1982(**inputs) - assert result.theta_4 == pytest.approx(expected['theta_4'], rel=rtol, abs=atol) - assert result.theta_7 == pytest.approx(expected['theta_7'], rel=rtol, abs=atol) - assert result.theta_10 == pytest.approx(expected['theta_10'], rel=rtol, abs=atol) - assert result.theta_20 == pytest.approx(expected['theta_20'], rel=rtol, abs=atol) - assert result.theta_33 == pytest.approx(expected['theta_33'], rel=rtol, abs=atol) - assert result.theta_60 == pytest.approx(expected['theta_60'], rel=rtol, abs=atol) - assert result.theta_100 == pytest.approx(expected['theta_100'], rel=rtol, abs=atol) - assert result.theta_200 == pytest.approx(expected['theta_200'], rel=rtol, abs=atol) - assert result.theta_400 == pytest.approx(expected['theta_400'], rel=rtol, abs=atol) - assert result.theta_700 == pytest.approx(expected['theta_700'], rel=rtol, abs=atol) - assert result.theta_1000 == pytest.approx(expected['theta_1000'], rel=rtol, abs=atol) - assert result.theta_1500 == pytest.approx(expected['theta_1500'], rel=rtol, abs=atol) + assert_close( + result.theta_4, + expected['theta_4'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_7, + expected['theta_7'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_10, + expected['theta_10'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_20, + expected['theta_20'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_33, + expected['theta_33'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_60, + expected['theta_60'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_100, + expected['theta_100'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_200, + expected['theta_200'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_400, + expected['theta_400'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_700, + expected['theta_700'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_1000, + expected['theta_1000'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_1500, + expected['theta_1500'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) def test_calc_full_wrc_rawls1982_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( - CASES_CALC_FULL_WRC_RAWLS1982, Rawls1982PTFResult - ) + inputs, expected, _out = prepare_vector_case(CASES_CALC_FULL_WRC_RAWLS1982, Rawls1982PTFResult) result = calc_full_wrc_rawls1982(**inputs, out=None) - assert result.theta_4[0] == pytest.approx(expected['theta_4'], rel=rtol, abs=atol) - assert result.theta_7[0] == pytest.approx(expected['theta_7'], rel=rtol, abs=atol) - assert result.theta_10[0] == pytest.approx(expected['theta_10'], rel=rtol, abs=atol) - assert result.theta_20[0] == pytest.approx(expected['theta_20'], rel=rtol, abs=atol) - assert result.theta_33[0] == pytest.approx(expected['theta_33'], rel=rtol, abs=atol) - assert result.theta_60[0] == pytest.approx(expected['theta_60'], rel=rtol, abs=atol) - assert result.theta_100[0] == pytest.approx(expected['theta_100'], rel=rtol, abs=atol) - assert result.theta_200[0] == pytest.approx(expected['theta_200'], rel=rtol, abs=atol) - assert result.theta_400[0] == pytest.approx(expected['theta_400'], rel=rtol, abs=atol) - assert result.theta_700[0] == pytest.approx(expected['theta_700'], rel=rtol, abs=atol) - assert result.theta_1000[0] == pytest.approx(expected['theta_1000'], rel=rtol, abs=atol) - assert result.theta_1500[0] == pytest.approx(expected['theta_1500'], rel=rtol, abs=atol) + assert_close( + result.theta_4[0], + expected['theta_4'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_7[0], + expected['theta_7'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_10[0], + expected['theta_10'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_20[0], + expected['theta_20'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_33[0], + expected['theta_33'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_60[0], + expected['theta_60'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_100[0], + expected['theta_100'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_200[0], + expected['theta_200'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_400[0], + expected['theta_400'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_700[0], + expected['theta_700'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_1000[0], + expected['theta_1000'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_1500[0], + expected['theta_1500'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) def test_calc_full_wrc_rawls1982_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( - CASES_CALC_FULL_WRC_RAWLS1982, Rawls1982PTFResult - ) + inputs, expected, out = prepare_vector_case(CASES_CALC_FULL_WRC_RAWLS1982, Rawls1982PTFResult) result = calc_full_wrc_rawls1982(**inputs, out=out) for actual, expected_out in zip(result, out, strict=True): assert actual is expected_out - assert result.theta_4[0] == pytest.approx(expected['theta_4'], rel=rtol, abs=atol) - assert result.theta_7[0] == pytest.approx(expected['theta_7'], rel=rtol, abs=atol) - assert result.theta_10[0] == pytest.approx(expected['theta_10'], rel=rtol, abs=atol) - assert result.theta_20[0] == pytest.approx(expected['theta_20'], rel=rtol, abs=atol) - assert result.theta_33[0] == pytest.approx(expected['theta_33'], rel=rtol, abs=atol) - assert result.theta_60[0] == pytest.approx(expected['theta_60'], rel=rtol, abs=atol) - assert result.theta_100[0] == pytest.approx(expected['theta_100'], rel=rtol, abs=atol) - assert result.theta_200[0] == pytest.approx(expected['theta_200'], rel=rtol, abs=atol) - assert result.theta_400[0] == pytest.approx(expected['theta_400'], rel=rtol, abs=atol) - assert result.theta_700[0] == pytest.approx(expected['theta_700'], rel=rtol, abs=atol) - assert result.theta_1000[0] == pytest.approx(expected['theta_1000'], rel=rtol, abs=atol) - assert result.theta_1500[0] == pytest.approx(expected['theta_1500'], rel=rtol, abs=atol) + assert_close( + result.theta_4[0], + expected['theta_4'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_7[0], + expected['theta_7'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_10[0], + expected['theta_10'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_20[0], + expected['theta_20'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_33[0], + expected['theta_33'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_60[0], + expected['theta_60'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_100[0], + expected['theta_100'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_200[0], + expected['theta_200'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_400[0], + expected['theta_400'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_700[0], + expected['theta_700'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_1000[0], + expected['theta_1000'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_1500[0], + expected['theta_1500'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) diff --git a/targets/ptfkit-py/tests/test_saxton2006.py b/targets/ptfkit-py/tests/test_saxton2006.py index 3602c9a..dc214b3 100644 --- a/targets/ptfkit-py/tests/test_saxton2006.py +++ b/targets/ptfkit-py/tests/test_saxton2006.py @@ -3,7 +3,7 @@ import pytest -from _helpers import prepare_vector_case +from _helpers import assert_close, prepare_vector_case from ptfkit.saxton2006 import ( Saxton2006DensityResult, Saxton2006GravelResult, @@ -23,99 +23,312 @@ ( {'clay': 0.05, 'organic_matter': 2.5, 'sand': 0.88}, { - 'air_entry_tension': 0.5986789729513293, - 'conductivity_lambda': 0.18776158355467926, - 'normal_density': 1.4264356197312436, + 'theta_1500': 0.05022058, + 'theta_33': 0.10282792364858702, + 'theta_s': 0.46172240764858724, 'plant_available_water': 0.05260734364858702, + 'air_entry_tension': 0.5986789729513293, 'retention_a': 0.00018076452552206025, 'retention_b': 5.325903100453899, + 'conductivity_lambda': 0.18776158355467926, 'saturated_conductivity': 108.1478278507403, - 'theta_1500': 0.05022058, - 'theta_33': 0.10282792364858702, - 'theta_s': 0.46172240764858724, + 'normal_density': 1.4264356197312436, }, - 1e-12, - 1e-12, ), ] +CASES_CALC_PTF_SAXTON2006_IDS = [ + 'table_3_sand', +] -@pytest.mark.parametrize(('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_SAXTON2006) -def test_calc_ptf_saxton2006_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float -): +@pytest.mark.parametrize( + ('inputs', 'expected'), CASES_CALC_PTF_SAXTON2006, ids=CASES_CALC_PTF_SAXTON2006_IDS +) +def test_calc_ptf_saxton2006_verification(inputs: dict[str, float], expected: dict[str, float]): result = calc_ptf_saxton2006(**inputs) - assert result.theta_1500 == pytest.approx(expected['theta_1500'], rel=rtol, abs=atol) - assert result.theta_33 == pytest.approx(expected['theta_33'], rel=rtol, abs=atol) - assert result.theta_s == pytest.approx(expected['theta_s'], rel=rtol, abs=atol) - assert result.plant_available_water == pytest.approx( - expected['plant_available_water'], rel=rtol, abs=atol - ) - assert result.air_entry_tension == pytest.approx( - expected['air_entry_tension'], rel=rtol, abs=atol - ) - assert result.retention_a == pytest.approx(expected['retention_a'], rel=rtol, abs=atol) - assert result.retention_b == pytest.approx(expected['retention_b'], rel=rtol, abs=atol) - assert result.conductivity_lambda == pytest.approx( - expected['conductivity_lambda'], rel=rtol, abs=atol + assert_close( + result.theta_1500, + expected['theta_1500'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_33, + expected['theta_33'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_s, + expected['theta_s'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.plant_available_water, + expected['plant_available_water'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.air_entry_tension, + expected['air_entry_tension'], + absolute=0.01, + relative=0.0, + quantity='matric_potential', + unit='kilopascal', + source='registry', + ) + assert_close( + result.retention_a, + expected['retention_a'], + absolute=1e-9, + relative=0.01, + quantity='retention_coefficient_a', + unit='kilopascal', + source='registry', + ) + assert_close( + result.retention_b, + expected['retention_b'], + absolute=0.001, + relative=0.0, + quantity='retention_coefficient_b', + unit='dimensionless', + source='registry', + ) + assert_close( + result.conductivity_lambda, + expected['conductivity_lambda'], + absolute=0.001, + relative=0.0, + quantity='conductivity_shape_parameter', + unit='dimensionless', + source='registry', + ) + assert_close( + result.saturated_conductivity, + expected['saturated_conductivity'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) + assert_close( + result.normal_density, + expected['normal_density'], + absolute=0.001, + relative=0.0, + quantity='dry_bulk_density', + unit='gram_per_cubic_centimeter', + source='registry', ) - assert result.saturated_conductivity == pytest.approx( - expected['saturated_conductivity'], rel=rtol, abs=atol - ) - assert result.normal_density == pytest.approx(expected['normal_density'], rel=rtol, abs=atol) def test_calc_ptf_saxton2006_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( - CASES_CALC_PTF_SAXTON2006, Saxton2006PTFResult - ) + inputs, expected, _out = prepare_vector_case(CASES_CALC_PTF_SAXTON2006, Saxton2006PTFResult) result = calc_ptf_saxton2006(**inputs, out=None) - assert result.theta_1500[0] == pytest.approx(expected['theta_1500'], rel=rtol, abs=atol) - assert result.theta_33[0] == pytest.approx(expected['theta_33'], rel=rtol, abs=atol) - assert result.theta_s[0] == pytest.approx(expected['theta_s'], rel=rtol, abs=atol) - assert result.plant_available_water[0] == pytest.approx( - expected['plant_available_water'], rel=rtol, abs=atol - ) - assert result.air_entry_tension[0] == pytest.approx( - expected['air_entry_tension'], rel=rtol, abs=atol - ) - assert result.retention_a[0] == pytest.approx(expected['retention_a'], rel=rtol, abs=atol) - assert result.retention_b[0] == pytest.approx(expected['retention_b'], rel=rtol, abs=atol) - assert result.conductivity_lambda[0] == pytest.approx( - expected['conductivity_lambda'], rel=rtol, abs=atol - ) - assert result.saturated_conductivity[0] == pytest.approx( - expected['saturated_conductivity'], rel=rtol, abs=atol + assert_close( + result.theta_1500[0], + expected['theta_1500'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_33[0], + expected['theta_33'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_s[0], + expected['theta_s'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.plant_available_water[0], + expected['plant_available_water'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.air_entry_tension[0], + expected['air_entry_tension'], + absolute=0.01, + relative=0.0, + quantity='matric_potential', + unit='kilopascal', + source='registry', + ) + assert_close( + result.retention_a[0], + expected['retention_a'], + absolute=1e-9, + relative=0.01, + quantity='retention_coefficient_a', + unit='kilopascal', + source='registry', + ) + assert_close( + result.retention_b[0], + expected['retention_b'], + absolute=0.001, + relative=0.0, + quantity='retention_coefficient_b', + unit='dimensionless', + source='registry', + ) + assert_close( + result.conductivity_lambda[0], + expected['conductivity_lambda'], + absolute=0.001, + relative=0.0, + quantity='conductivity_shape_parameter', + unit='dimensionless', + source='registry', + ) + assert_close( + result.saturated_conductivity[0], + expected['saturated_conductivity'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) + assert_close( + result.normal_density[0], + expected['normal_density'], + absolute=0.001, + relative=0.0, + quantity='dry_bulk_density', + unit='gram_per_cubic_centimeter', + source='registry', ) - assert result.normal_density[0] == pytest.approx(expected['normal_density'], rel=rtol, abs=atol) def test_calc_ptf_saxton2006_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( - CASES_CALC_PTF_SAXTON2006, Saxton2006PTFResult - ) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_SAXTON2006, Saxton2006PTFResult) result = calc_ptf_saxton2006(**inputs, out=out) for actual, expected_out in zip(result, out, strict=True): assert actual is expected_out - assert result.theta_1500[0] == pytest.approx(expected['theta_1500'], rel=rtol, abs=atol) - assert result.theta_33[0] == pytest.approx(expected['theta_33'], rel=rtol, abs=atol) - assert result.theta_s[0] == pytest.approx(expected['theta_s'], rel=rtol, abs=atol) - assert result.plant_available_water[0] == pytest.approx( - expected['plant_available_water'], rel=rtol, abs=atol - ) - assert result.air_entry_tension[0] == pytest.approx( - expected['air_entry_tension'], rel=rtol, abs=atol - ) - assert result.retention_a[0] == pytest.approx(expected['retention_a'], rel=rtol, abs=atol) - assert result.retention_b[0] == pytest.approx(expected['retention_b'], rel=rtol, abs=atol) - assert result.conductivity_lambda[0] == pytest.approx( - expected['conductivity_lambda'], rel=rtol, abs=atol - ) - assert result.saturated_conductivity[0] == pytest.approx( - expected['saturated_conductivity'], rel=rtol, abs=atol + assert_close( + result.theta_1500[0], + expected['theta_1500'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_33[0], + expected['theta_33'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_s[0], + expected['theta_s'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.plant_available_water[0], + expected['plant_available_water'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.air_entry_tension[0], + expected['air_entry_tension'], + absolute=0.01, + relative=0.0, + quantity='matric_potential', + unit='kilopascal', + source='registry', + ) + assert_close( + result.retention_a[0], + expected['retention_a'], + absolute=1e-9, + relative=0.01, + quantity='retention_coefficient_a', + unit='kilopascal', + source='registry', + ) + assert_close( + result.retention_b[0], + expected['retention_b'], + absolute=0.001, + relative=0.0, + quantity='retention_coefficient_b', + unit='dimensionless', + source='registry', + ) + assert_close( + result.conductivity_lambda[0], + expected['conductivity_lambda'], + absolute=0.001, + relative=0.0, + quantity='conductivity_shape_parameter', + unit='dimensionless', + source='registry', + ) + assert_close( + result.saturated_conductivity[0], + expected['saturated_conductivity'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) + assert_close( + result.normal_density[0], + expected['normal_density'], + absolute=0.001, + relative=0.0, + quantity='dry_bulk_density', + unit='gram_per_cubic_centimeter', + source='registry', ) - assert result.normal_density[0] == pytest.approx(expected['normal_density'], rel=rtol, abs=atol) CASES_CALC_DENSITY_ADJUSTMENT_SAXTON2006 = [ @@ -128,75 +341,150 @@ def test_calc_ptf_saxton2006_out(): }, { 'adjusted_density': 1.569079181704368, - 'adjusted_theta_33': 0.09206237180155877, 'adjusted_theta_s': 0.407894648413446, + 'adjusted_theta_33': 0.09206237180155877, 'adjusted_theta_s_minus_33': 0.3158322766118873, }, - 1e-12, - 1e-12, ), ] +CASES_CALC_DENSITY_ADJUSTMENT_SAXTON2006_IDS = [ + 'table_3_sand_compacted', +] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_DENSITY_ADJUSTMENT_SAXTON2006 + ('inputs', 'expected'), + CASES_CALC_DENSITY_ADJUSTMENT_SAXTON2006, + ids=CASES_CALC_DENSITY_ADJUSTMENT_SAXTON2006_IDS, ) -def test_calc_density_adjustment_saxton2006_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_density_adjustment_saxton2006_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_density_adjustment_saxton2006(**inputs) - assert result.adjusted_density == pytest.approx( - expected['adjusted_density'], rel=rtol, abs=atol - ) - assert result.adjusted_theta_s == pytest.approx( - expected['adjusted_theta_s'], rel=rtol, abs=atol - ) - assert result.adjusted_theta_33 == pytest.approx( - expected['adjusted_theta_33'], rel=rtol, abs=atol - ) - assert result.adjusted_theta_s_minus_33 == pytest.approx( - expected['adjusted_theta_s_minus_33'], rel=rtol, abs=atol + assert_close( + result.adjusted_density, + expected['adjusted_density'], + absolute=0.001, + relative=0.0, + quantity='dry_bulk_density', + unit='gram_per_cubic_centimeter', + source='registry', + ) + assert_close( + result.adjusted_theta_s, + expected['adjusted_theta_s'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.adjusted_theta_33, + expected['adjusted_theta_33'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.adjusted_theta_s_minus_33, + expected['adjusted_theta_s_minus_33'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', ) def test_calc_density_adjustment_saxton2006_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_DENSITY_ADJUSTMENT_SAXTON2006, Saxton2006DensityResult ) result = calc_density_adjustment_saxton2006(**inputs, out=None) - assert result.adjusted_density[0] == pytest.approx( - expected['adjusted_density'], rel=rtol, abs=atol - ) - assert result.adjusted_theta_s[0] == pytest.approx( - expected['adjusted_theta_s'], rel=rtol, abs=atol - ) - assert result.adjusted_theta_33[0] == pytest.approx( - expected['adjusted_theta_33'], rel=rtol, abs=atol - ) - assert result.adjusted_theta_s_minus_33[0] == pytest.approx( - expected['adjusted_theta_s_minus_33'], rel=rtol, abs=atol + assert_close( + result.adjusted_density[0], + expected['adjusted_density'], + absolute=0.001, + relative=0.0, + quantity='dry_bulk_density', + unit='gram_per_cubic_centimeter', + source='registry', + ) + assert_close( + result.adjusted_theta_s[0], + expected['adjusted_theta_s'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.adjusted_theta_33[0], + expected['adjusted_theta_33'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.adjusted_theta_s_minus_33[0], + expected['adjusted_theta_s_minus_33'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', ) def test_calc_density_adjustment_saxton2006_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_DENSITY_ADJUSTMENT_SAXTON2006, Saxton2006DensityResult ) result = calc_density_adjustment_saxton2006(**inputs, out=out) for actual, expected_out in zip(result, out, strict=True): assert actual is expected_out - assert result.adjusted_density[0] == pytest.approx( - expected['adjusted_density'], rel=rtol, abs=atol - ) - assert result.adjusted_theta_s[0] == pytest.approx( - expected['adjusted_theta_s'], rel=rtol, abs=atol - ) - assert result.adjusted_theta_33[0] == pytest.approx( - expected['adjusted_theta_33'], rel=rtol, abs=atol - ) - assert result.adjusted_theta_s_minus_33[0] == pytest.approx( - expected['adjusted_theta_s_minus_33'], rel=rtol, abs=atol + assert_close( + result.adjusted_density[0], + expected['adjusted_density'], + absolute=0.001, + relative=0.0, + quantity='dry_bulk_density', + unit='gram_per_cubic_centimeter', + source='registry', + ) + assert_close( + result.adjusted_theta_s[0], + expected['adjusted_theta_s'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.adjusted_theta_33[0], + expected['adjusted_theta_33'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.adjusted_theta_s_minus_33[0], + expected['adjusted_theta_s_minus_33'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', ) @@ -204,32 +492,61 @@ def test_calc_density_adjustment_saxton2006_out(): ( {'theta': 0.07652425182429351, 'theta_1500': 0.05022058, 'theta_33': 0.10282792364858702}, {'tension': 159.18094591362183}, - 1e-12, - 1e-12, ), ] +CASES_CALC_TENSION_DRY_SAXTON2006_IDS = [ + 'table_3_sand_midpoint', +] -@pytest.mark.parametrize(('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_TENSION_DRY_SAXTON2006) -def test_calc_tension_dry_saxton2006_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +@pytest.mark.parametrize( + ('inputs', 'expected'), + CASES_CALC_TENSION_DRY_SAXTON2006, + ids=CASES_CALC_TENSION_DRY_SAXTON2006_IDS, +) +def test_calc_tension_dry_saxton2006_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_tension_dry_saxton2006(**inputs) - assert result == pytest.approx(expected['tension'], rel=rtol, abs=atol) + assert_close( + result, + expected['tension'], + absolute=0.01, + relative=0.0, + quantity='matric_potential', + unit='kilopascal', + source='registry', + ) def test_calc_tension_dry_saxton2006_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case(CASES_CALC_TENSION_DRY_SAXTON2006) + inputs, expected, _out = prepare_vector_case(CASES_CALC_TENSION_DRY_SAXTON2006) result = calc_tension_dry_saxton2006(**inputs, out=None) - assert result[0] == pytest.approx(expected['tension'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['tension'], + absolute=0.01, + relative=0.0, + quantity='matric_potential', + unit='kilopascal', + source='registry', + ) def test_calc_tension_dry_saxton2006_out(): - inputs, expected, rtol, atol, out = prepare_vector_case(CASES_CALC_TENSION_DRY_SAXTON2006) + inputs, expected, out = prepare_vector_case(CASES_CALC_TENSION_DRY_SAXTON2006) result = calc_tension_dry_saxton2006(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['tension'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['tension'], + absolute=0.01, + relative=0.0, + quantity='matric_potential', + unit='kilopascal', + source='registry', + ) CASES_CALC_TENSION_WET_SAXTON2006 = [ @@ -241,32 +558,61 @@ def test_calc_tension_dry_saxton2006_out(): 'theta_s': 0.46172240764858724, }, {'tension': 24.227216407352152}, - 1e-12, - 1e-12, ), ] +CASES_CALC_TENSION_WET_SAXTON2006_IDS = [ + 'table_3_sand_theta_0_2', +] -@pytest.mark.parametrize(('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_TENSION_WET_SAXTON2006) -def test_calc_tension_wet_saxton2006_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +@pytest.mark.parametrize( + ('inputs', 'expected'), + CASES_CALC_TENSION_WET_SAXTON2006, + ids=CASES_CALC_TENSION_WET_SAXTON2006_IDS, +) +def test_calc_tension_wet_saxton2006_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_tension_wet_saxton2006(**inputs) - assert result == pytest.approx(expected['tension'], rel=rtol, abs=atol) + assert_close( + result, + expected['tension'], + absolute=0.01, + relative=0.0, + quantity='matric_potential', + unit='kilopascal', + source='registry', + ) def test_calc_tension_wet_saxton2006_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case(CASES_CALC_TENSION_WET_SAXTON2006) + inputs, expected, _out = prepare_vector_case(CASES_CALC_TENSION_WET_SAXTON2006) result = calc_tension_wet_saxton2006(**inputs, out=None) - assert result[0] == pytest.approx(expected['tension'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['tension'], + absolute=0.01, + relative=0.0, + quantity='matric_potential', + unit='kilopascal', + source='registry', + ) def test_calc_tension_wet_saxton2006_out(): - inputs, expected, rtol, atol, out = prepare_vector_case(CASES_CALC_TENSION_WET_SAXTON2006) + inputs, expected, out = prepare_vector_case(CASES_CALC_TENSION_WET_SAXTON2006) result = calc_tension_wet_saxton2006(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['tension'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['tension'], + absolute=0.01, + relative=0.0, + quantity='matric_potential', + unit='kilopascal', + source='registry', + ) CASES_CALC_CONDUCTIVITY_SAXTON2006 = [ @@ -278,32 +624,61 @@ def test_calc_tension_wet_saxton2006_out(): 'theta_s': 0.46172240764858724, }, {'conductivity': 0.3003203142764693}, - 1e-12, - 1e-12, ), ] +CASES_CALC_CONDUCTIVITY_SAXTON2006_IDS = [ + 'table_3_sand_theta_0_3', +] -@pytest.mark.parametrize(('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_CONDUCTIVITY_SAXTON2006) -def test_calc_conductivity_saxton2006_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +@pytest.mark.parametrize( + ('inputs', 'expected'), + CASES_CALC_CONDUCTIVITY_SAXTON2006, + ids=CASES_CALC_CONDUCTIVITY_SAXTON2006_IDS, +) +def test_calc_conductivity_saxton2006_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_conductivity_saxton2006(**inputs) - assert result == pytest.approx(expected['conductivity'], rel=rtol, abs=atol) + assert_close( + result, + expected['conductivity'], + absolute=0.0001, + relative=0.01, + quantity='unsaturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_conductivity_saxton2006_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case(CASES_CALC_CONDUCTIVITY_SAXTON2006) + inputs, expected, _out = prepare_vector_case(CASES_CALC_CONDUCTIVITY_SAXTON2006) result = calc_conductivity_saxton2006(**inputs, out=None) - assert result[0] == pytest.approx(expected['conductivity'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['conductivity'], + absolute=0.0001, + relative=0.01, + quantity='unsaturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) def test_calc_conductivity_saxton2006_out(): - inputs, expected, rtol, atol, out = prepare_vector_case(CASES_CALC_CONDUCTIVITY_SAXTON2006) + inputs, expected, out = prepare_vector_case(CASES_CALC_CONDUCTIVITY_SAXTON2006) result = calc_conductivity_saxton2006(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['conductivity'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['conductivity'], + absolute=0.0001, + relative=0.01, + quantity='unsaturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', + ) CASES_CALC_GRAVEL_ADJUSTMENT_SAXTON2006 = [ @@ -315,122 +690,242 @@ def test_calc_conductivity_saxton2006_out(): 'saturated_conductivity': 108.1478278507403, }, { + 'gravel_volume_fraction': 0.11860834455314037, 'bulk_density': 1.5715605653291098, 'bulk_plant_available_water': 0.04636767370708995, 'bulk_saturated_conductivity': 97.54880510583706, - 'gravel_volume_fraction': 0.11860834455314037, }, - 1e-12, - 1e-12, ), ] +CASES_CALC_GRAVEL_ADJUSTMENT_SAXTON2006_IDS = [ + 'table_3_sand_twenty_percent_gravel', +] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_GRAVEL_ADJUSTMENT_SAXTON2006 + ('inputs', 'expected'), + CASES_CALC_GRAVEL_ADJUSTMENT_SAXTON2006, + ids=CASES_CALC_GRAVEL_ADJUSTMENT_SAXTON2006_IDS, ) -def test_calc_gravel_adjustment_saxton2006_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_gravel_adjustment_saxton2006_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_gravel_adjustment_saxton2006(**inputs) - assert result.gravel_volume_fraction == pytest.approx( - expected['gravel_volume_fraction'], rel=rtol, abs=atol - ) - assert result.bulk_density == pytest.approx(expected['bulk_density'], rel=rtol, abs=atol) - assert result.bulk_plant_available_water == pytest.approx( - expected['bulk_plant_available_water'], rel=rtol, abs=atol - ) - assert result.bulk_saturated_conductivity == pytest.approx( - expected['bulk_saturated_conductivity'], rel=rtol, abs=atol + assert_close( + result.gravel_volume_fraction, + expected['gravel_volume_fraction'], + absolute=0.001, + relative=0.0, + quantity='gravel_volume_fraction', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.bulk_density, + expected['bulk_density'], + absolute=0.001, + relative=0.0, + quantity='dry_bulk_density', + unit='gram_per_cubic_centimeter', + source='registry', + ) + assert_close( + result.bulk_plant_available_water, + expected['bulk_plant_available_water'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.bulk_saturated_conductivity, + expected['bulk_saturated_conductivity'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', ) def test_calc_gravel_adjustment_saxton2006_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_GRAVEL_ADJUSTMENT_SAXTON2006, Saxton2006GravelResult ) result = calc_gravel_adjustment_saxton2006(**inputs, out=None) - assert result.gravel_volume_fraction[0] == pytest.approx( - expected['gravel_volume_fraction'], rel=rtol, abs=atol - ) - assert result.bulk_density[0] == pytest.approx(expected['bulk_density'], rel=rtol, abs=atol) - assert result.bulk_plant_available_water[0] == pytest.approx( - expected['bulk_plant_available_water'], rel=rtol, abs=atol - ) - assert result.bulk_saturated_conductivity[0] == pytest.approx( - expected['bulk_saturated_conductivity'], rel=rtol, abs=atol + assert_close( + result.gravel_volume_fraction[0], + expected['gravel_volume_fraction'], + absolute=0.001, + relative=0.0, + quantity='gravel_volume_fraction', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.bulk_density[0], + expected['bulk_density'], + absolute=0.001, + relative=0.0, + quantity='dry_bulk_density', + unit='gram_per_cubic_centimeter', + source='registry', + ) + assert_close( + result.bulk_plant_available_water[0], + expected['bulk_plant_available_water'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.bulk_saturated_conductivity[0], + expected['bulk_saturated_conductivity'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', ) def test_calc_gravel_adjustment_saxton2006_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_GRAVEL_ADJUSTMENT_SAXTON2006, Saxton2006GravelResult ) result = calc_gravel_adjustment_saxton2006(**inputs, out=out) for actual, expected_out in zip(result, out, strict=True): assert actual is expected_out - assert result.gravel_volume_fraction[0] == pytest.approx( - expected['gravel_volume_fraction'], rel=rtol, abs=atol - ) - assert result.bulk_density[0] == pytest.approx(expected['bulk_density'], rel=rtol, abs=atol) - assert result.bulk_plant_available_water[0] == pytest.approx( - expected['bulk_plant_available_water'], rel=rtol, abs=atol - ) - assert result.bulk_saturated_conductivity[0] == pytest.approx( - expected['bulk_saturated_conductivity'], rel=rtol, abs=atol + assert_close( + result.gravel_volume_fraction[0], + expected['gravel_volume_fraction'], + absolute=0.001, + relative=0.0, + quantity='gravel_volume_fraction', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.bulk_density[0], + expected['bulk_density'], + absolute=0.001, + relative=0.0, + quantity='dry_bulk_density', + unit='gram_per_cubic_centimeter', + source='registry', + ) + assert_close( + result.bulk_plant_available_water[0], + expected['bulk_plant_available_water'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.bulk_saturated_conductivity[0], + expected['bulk_saturated_conductivity'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='millimeter_per_hour', + source='registry', ) CASES_CALC_OSMOTIC_POTENTIAL_SAXTON2006 = [ ( {'electrical_conductivity': 4.0, 'theta': 0.3, 'theta_s': 0.46172240764858724}, - {'osmotic_potential': 221.6267556713219, 'saturated_osmotic_potential': 144.0}, - 1e-12, - 1e-12, + {'saturated_osmotic_potential': 144.0, 'osmotic_potential': 221.6267556713219}, ), ] +CASES_CALC_OSMOTIC_POTENTIAL_SAXTON2006_IDS = [ + 'saline_partly_saturated_soil', +] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_OSMOTIC_POTENTIAL_SAXTON2006 + ('inputs', 'expected'), + CASES_CALC_OSMOTIC_POTENTIAL_SAXTON2006, + ids=CASES_CALC_OSMOTIC_POTENTIAL_SAXTON2006_IDS, ) -def test_calc_osmotic_potential_saxton2006_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_osmotic_potential_saxton2006_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_osmotic_potential_saxton2006(**inputs) - assert result.saturated_osmotic_potential == pytest.approx( - expected['saturated_osmotic_potential'], rel=rtol, abs=atol + assert_close( + result.saturated_osmotic_potential, + expected['saturated_osmotic_potential'], + absolute=0.01, + relative=0.0, + quantity='osmotic_potential', + unit='kilopascal', + source='registry', ) - assert result.osmotic_potential == pytest.approx( - expected['osmotic_potential'], rel=rtol, abs=atol + assert_close( + result.osmotic_potential, + expected['osmotic_potential'], + absolute=0.01, + relative=0.0, + quantity='osmotic_potential', + unit='kilopascal', + source='registry', ) def test_calc_osmotic_potential_saxton2006_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_OSMOTIC_POTENTIAL_SAXTON2006, Saxton2006SalinityResult ) result = calc_osmotic_potential_saxton2006(**inputs, out=None) - assert result.saturated_osmotic_potential[0] == pytest.approx( - expected['saturated_osmotic_potential'], rel=rtol, abs=atol - ) - assert result.osmotic_potential[0] == pytest.approx( - expected['osmotic_potential'], rel=rtol, abs=atol + assert_close( + result.saturated_osmotic_potential[0], + expected['saturated_osmotic_potential'], + absolute=0.01, + relative=0.0, + quantity='osmotic_potential', + unit='kilopascal', + source='registry', + ) + assert_close( + result.osmotic_potential[0], + expected['osmotic_potential'], + absolute=0.01, + relative=0.0, + quantity='osmotic_potential', + unit='kilopascal', + source='registry', ) def test_calc_osmotic_potential_saxton2006_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_OSMOTIC_POTENTIAL_SAXTON2006, Saxton2006SalinityResult ) result = calc_osmotic_potential_saxton2006(**inputs, out=out) for actual, expected_out in zip(result, out, strict=True): assert actual is expected_out - assert result.saturated_osmotic_potential[0] == pytest.approx( - expected['saturated_osmotic_potential'], rel=rtol, abs=atol - ) - assert result.osmotic_potential[0] == pytest.approx( - expected['osmotic_potential'], rel=rtol, abs=atol + assert_close( + result.saturated_osmotic_potential[0], + expected['saturated_osmotic_potential'], + absolute=0.01, + relative=0.0, + quantity='osmotic_potential', + unit='kilopascal', + source='registry', + ) + assert_close( + result.osmotic_potential[0], + expected['osmotic_potential'], + absolute=0.01, + relative=0.0, + quantity='osmotic_potential', + unit='kilopascal', + source='registry', ) diff --git a/targets/ptfkit-py/tests/test_tiwary2014.py b/targets/ptfkit-py/tests/test_tiwary2014.py index 19a77ef..5a6e479 100644 --- a/targets/ptfkit-py/tests/test_tiwary2014.py +++ b/targets/ptfkit-py/tests/test_tiwary2014.py @@ -3,35 +3,62 @@ import pytest -from _helpers import prepare_vector_case +from _helpers import assert_close, prepare_vector_case from ptfkit.tiwary2014 import Tiwary2014PTFResult, calc_ptf_tiwary2014_bsr, calc_ptf_tiwary2014_igp CASES_CALC_PTF_TIWARY2014_IGP = [ - ({'bulk_density': 1.674, 'esp': 4.6, 'sand': 37.3}, {'k_sat': 5.103578e-7}, 1e-10, 1e-12), + ({'bulk_density': 1.674, 'esp': 4.6, 'sand': 37.3}, {'k_sat': 5.103578e-7}), +] +CASES_CALC_PTF_TIWARY2014_IGP_IDS = [ + 'igp_saturated_conductivity_case', ] -@pytest.mark.parametrize(('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_TIWARY2014_IGP) -def test_calc_ptf_tiwary2014_igp_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float -): +@pytest.mark.parametrize( + ('inputs', 'expected'), CASES_CALC_PTF_TIWARY2014_IGP, ids=CASES_CALC_PTF_TIWARY2014_IGP_IDS +) +def test_calc_ptf_tiwary2014_igp_verification(inputs: dict[str, float], expected: dict[str, float]): result = calc_ptf_tiwary2014_igp(**inputs) - assert result == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result, + expected['k_sat'], + absolute=1e-10, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='meter_per_second', + source='registry', + ) def test_calc_ptf_tiwary2014_igp_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case(CASES_CALC_PTF_TIWARY2014_IGP) + inputs, expected, _out = prepare_vector_case(CASES_CALC_PTF_TIWARY2014_IGP) result = calc_ptf_tiwary2014_igp(**inputs, out=None) - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=1e-10, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='meter_per_second', + source='registry', + ) def test_calc_ptf_tiwary2014_igp_out(): - inputs, expected, rtol, atol, out = prepare_vector_case(CASES_CALC_PTF_TIWARY2014_IGP) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_TIWARY2014_IGP) result = calc_ptf_tiwary2014_igp(**inputs, out=out) assert result is out - assert result[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result[0], + expected['k_sat'], + absolute=1e-10, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='meter_per_second', + source='registry', + ) CASES_CALC_PTF_TIWARY2014_BSR = [ @@ -44,44 +71,137 @@ def test_calc_ptf_tiwary2014_igp_out(): 'excm': 3.32, 'ph': 7.6, }, - {'k_sat': 5.373367e-6, 'w_100': 36.8273, 'w_1500': 21.6976, 'w_33': 41.1729}, - 1e-10, - 1e-12, + {'w_33': 41.1729, 'w_100': 36.8273, 'w_1500': 21.6976, 'k_sat': 5.373367e-6}, ), ] +CASES_CALC_PTF_TIWARY2014_BSR_IDS = [ + 'black_soil_compatibility_case', +] -@pytest.mark.parametrize(('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_TIWARY2014_BSR) -def test_calc_ptf_tiwary2014_bsr_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float -): +@pytest.mark.parametrize( + ('inputs', 'expected'), CASES_CALC_PTF_TIWARY2014_BSR, ids=CASES_CALC_PTF_TIWARY2014_BSR_IDS +) +def test_calc_ptf_tiwary2014_bsr_verification(inputs: dict[str, float], expected: dict[str, float]): result = calc_ptf_tiwary2014_bsr(**inputs) - assert result.w_33 == pytest.approx(expected['w_33'], rel=rtol, abs=atol) - assert result.w_100 == pytest.approx(expected['w_100'], rel=rtol, abs=atol) - assert result.w_1500 == pytest.approx(expected['w_1500'], rel=rtol, abs=atol) - assert result.k_sat == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result.w_33, + expected['w_33'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) + assert_close( + result.w_100, + expected['w_100'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) + assert_close( + result.w_1500, + expected['w_1500'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) + assert_close( + result.k_sat, + expected['k_sat'], + absolute=1e-10, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='meter_per_second', + source='registry', + ) def test_calc_ptf_tiwary2014_bsr_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( - CASES_CALC_PTF_TIWARY2014_BSR, Tiwary2014PTFResult - ) + inputs, expected, _out = prepare_vector_case(CASES_CALC_PTF_TIWARY2014_BSR, Tiwary2014PTFResult) result = calc_ptf_tiwary2014_bsr(**inputs, out=None) - assert result.w_33[0] == pytest.approx(expected['w_33'], rel=rtol, abs=atol) - assert result.w_100[0] == pytest.approx(expected['w_100'], rel=rtol, abs=atol) - assert result.w_1500[0] == pytest.approx(expected['w_1500'], rel=rtol, abs=atol) - assert result.k_sat[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result.w_33[0], + expected['w_33'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) + assert_close( + result.w_100[0], + expected['w_100'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) + assert_close( + result.w_1500[0], + expected['w_1500'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) + assert_close( + result.k_sat[0], + expected['k_sat'], + absolute=1e-10, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='meter_per_second', + source='registry', + ) def test_calc_ptf_tiwary2014_bsr_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( - CASES_CALC_PTF_TIWARY2014_BSR, Tiwary2014PTFResult - ) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_TIWARY2014_BSR, Tiwary2014PTFResult) result = calc_ptf_tiwary2014_bsr(**inputs, out=out) for actual, expected_out in zip(result, out, strict=True): assert actual is expected_out - assert result.w_33[0] == pytest.approx(expected['w_33'], rel=rtol, abs=atol) - assert result.w_100[0] == pytest.approx(expected['w_100'], rel=rtol, abs=atol) - assert result.w_1500[0] == pytest.approx(expected['w_1500'], rel=rtol, abs=atol) - assert result.k_sat[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result.w_33[0], + expected['w_33'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) + assert_close( + result.w_100[0], + expected['w_100'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) + assert_close( + result.w_1500[0], + expected['w_1500'], + absolute=0.1, + relative=0.0, + quantity='gravimetric_water_content', + unit='mass_percent', + source='registry', + ) + assert_close( + result.k_sat[0], + expected['k_sat'], + absolute=1e-10, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='meter_per_second', + source='registry', + ) diff --git a/targets/ptfkit-py/tests/test_varallyai1982.py b/targets/ptfkit-py/tests/test_varallyai1982.py index a6a3274..b8191d9 100644 --- a/targets/ptfkit-py/tests/test_varallyai1982.py +++ b/targets/ptfkit-py/tests/test_varallyai1982.py @@ -3,7 +3,7 @@ import pytest -from _helpers import prepare_vector_case +from _helpers import assert_close, prepare_vector_case from ptfkit.varallyai1982 import ( Varallyai1982Parameters, calc_ptf_varallyai1982_chernozem_a, @@ -16,178 +16,478 @@ CASES_CALC_PTF_VARALLYAI1982_MEADOW = [ ( {'bulk_density': 1.4, 'fine_fraction': 0.25, 'fine_sand_fraction': 0.3}, - {'m': 0.10029, 'pf_star': 3.62445, 'theta_0': 46.54495}, - 1e-12, - 1e-12, + {'theta_0': 46.54495, 'm': 0.10029, 'pf_star': 3.62445}, ), ] +CASES_CALC_PTF_VARALLYAI1982_MEADOW_IDS = [ + 'representative_review_case', +] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_VARALLYAI1982_MEADOW + ('inputs', 'expected'), + CASES_CALC_PTF_VARALLYAI1982_MEADOW, + ids=CASES_CALC_PTF_VARALLYAI1982_MEADOW_IDS, ) -def test_calc_ptf_varallyai1982_meadow_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_varallyai1982_meadow_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_varallyai1982_meadow(**inputs) - assert result.theta_0 == pytest.approx(expected['theta_0'], rel=rtol, abs=atol) - assert result.m == pytest.approx(expected['m'], rel=rtol, abs=atol) - assert result.pf_star == pytest.approx(expected['pf_star'], rel=rtol, abs=atol) + assert_close( + result.theta_0, + expected['theta_0'], + absolute=0.1, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_percent', + source='registry', + ) + assert_close( + result.m, + expected['m'], + absolute=0.001, + relative=0.0, + quantity='varallyai_m', + unit='dimensionless', + source='registry', + ) + assert_close( + result.pf_star, + expected['pf_star'], + absolute=0.001, + relative=0.0, + quantity='varallyai_pf_star', + unit='dimensionless', + source='registry', + ) def test_calc_ptf_varallyai1982_meadow_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_VARALLYAI1982_MEADOW, Varallyai1982Parameters ) result = calc_ptf_varallyai1982_meadow(**inputs, out=None) - assert result.theta_0[0] == pytest.approx(expected['theta_0'], rel=rtol, abs=atol) - assert result.m[0] == pytest.approx(expected['m'], rel=rtol, abs=atol) - assert result.pf_star[0] == pytest.approx(expected['pf_star'], rel=rtol, abs=atol) + assert_close( + result.theta_0[0], + expected['theta_0'], + absolute=0.1, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_percent', + source='registry', + ) + assert_close( + result.m[0], + expected['m'], + absolute=0.001, + relative=0.0, + quantity='varallyai_m', + unit='dimensionless', + source='registry', + ) + assert_close( + result.pf_star[0], + expected['pf_star'], + absolute=0.001, + relative=0.0, + quantity='varallyai_pf_star', + unit='dimensionless', + source='registry', + ) def test_calc_ptf_varallyai1982_meadow_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_VARALLYAI1982_MEADOW, Varallyai1982Parameters ) result = calc_ptf_varallyai1982_meadow(**inputs, out=out) for actual, expected_out in zip(result, out, strict=True): assert actual is expected_out - assert result.theta_0[0] == pytest.approx(expected['theta_0'], rel=rtol, abs=atol) - assert result.m[0] == pytest.approx(expected['m'], rel=rtol, abs=atol) - assert result.pf_star[0] == pytest.approx(expected['pf_star'], rel=rtol, abs=atol) + assert_close( + result.theta_0[0], + expected['theta_0'], + absolute=0.1, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_percent', + source='registry', + ) + assert_close( + result.m[0], + expected['m'], + absolute=0.001, + relative=0.0, + quantity='varallyai_m', + unit='dimensionless', + source='registry', + ) + assert_close( + result.pf_star[0], + expected['pf_star'], + absolute=0.001, + relative=0.0, + quantity='varallyai_pf_star', + unit='dimensionless', + source='registry', + ) CASES_CALC_PTF_VARALLYAI1982_CHERNOZEM_A = [ ( {'bulk_density': 1.4, 'fine_fraction': 0.35}, - {'m': 0.4174, 'pf_star': 4.00469, 'theta_0': 52.005}, - 1e-12, - 1e-12, + {'theta_0': 52.005, 'm': 0.4174, 'pf_star': 4.00469}, ), ] +CASES_CALC_PTF_VARALLYAI1982_CHERNOZEM_A_IDS = [ + 'representative_review_case', +] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_VARALLYAI1982_CHERNOZEM_A + ('inputs', 'expected'), + CASES_CALC_PTF_VARALLYAI1982_CHERNOZEM_A, + ids=CASES_CALC_PTF_VARALLYAI1982_CHERNOZEM_A_IDS, ) -def test_calc_ptf_varallyai1982_chernozem_a_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_varallyai1982_chernozem_a_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_varallyai1982_chernozem_a(**inputs) - assert result.theta_0 == pytest.approx(expected['theta_0'], rel=rtol, abs=atol) - assert result.m == pytest.approx(expected['m'], rel=rtol, abs=atol) - assert result.pf_star == pytest.approx(expected['pf_star'], rel=rtol, abs=atol) + assert_close( + result.theta_0, + expected['theta_0'], + absolute=0.1, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_percent', + source='registry', + ) + assert_close( + result.m, + expected['m'], + absolute=0.001, + relative=0.0, + quantity='varallyai_m', + unit='dimensionless', + source='registry', + ) + assert_close( + result.pf_star, + expected['pf_star'], + absolute=0.001, + relative=0.0, + quantity='varallyai_pf_star', + unit='dimensionless', + source='registry', + ) def test_calc_ptf_varallyai1982_chernozem_a_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_VARALLYAI1982_CHERNOZEM_A, Varallyai1982Parameters ) result = calc_ptf_varallyai1982_chernozem_a(**inputs, out=None) - assert result.theta_0[0] == pytest.approx(expected['theta_0'], rel=rtol, abs=atol) - assert result.m[0] == pytest.approx(expected['m'], rel=rtol, abs=atol) - assert result.pf_star[0] == pytest.approx(expected['pf_star'], rel=rtol, abs=atol) + assert_close( + result.theta_0[0], + expected['theta_0'], + absolute=0.1, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_percent', + source='registry', + ) + assert_close( + result.m[0], + expected['m'], + absolute=0.001, + relative=0.0, + quantity='varallyai_m', + unit='dimensionless', + source='registry', + ) + assert_close( + result.pf_star[0], + expected['pf_star'], + absolute=0.001, + relative=0.0, + quantity='varallyai_pf_star', + unit='dimensionless', + source='registry', + ) def test_calc_ptf_varallyai1982_chernozem_a_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_VARALLYAI1982_CHERNOZEM_A, Varallyai1982Parameters ) result = calc_ptf_varallyai1982_chernozem_a(**inputs, out=out) for actual, expected_out in zip(result, out, strict=True): assert actual is expected_out - assert result.theta_0[0] == pytest.approx(expected['theta_0'], rel=rtol, abs=atol) - assert result.m[0] == pytest.approx(expected['m'], rel=rtol, abs=atol) - assert result.pf_star[0] == pytest.approx(expected['pf_star'], rel=rtol, abs=atol) + assert_close( + result.theta_0[0], + expected['theta_0'], + absolute=0.1, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_percent', + source='registry', + ) + assert_close( + result.m[0], + expected['m'], + absolute=0.001, + relative=0.0, + quantity='varallyai_m', + unit='dimensionless', + source='registry', + ) + assert_close( + result.pf_star[0], + expected['pf_star'], + absolute=0.001, + relative=0.0, + quantity='varallyai_pf_star', + unit='dimensionless', + source='registry', + ) CASES_CALC_PTF_VARALLYAI1982_CHERNOZEM_B = [ ( {'bulk_density': 1.4, 'fine_fraction': 0.35}, - {'m': 0.407, 'pf_star': 3.9299, 'theta_0': 47.60035}, - 1e-12, - 1e-12, + {'theta_0': 47.60035, 'm': 0.407, 'pf_star': 3.9299}, ), ] +CASES_CALC_PTF_VARALLYAI1982_CHERNOZEM_B_IDS = [ + 'representative_review_case', +] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_VARALLYAI1982_CHERNOZEM_B + ('inputs', 'expected'), + CASES_CALC_PTF_VARALLYAI1982_CHERNOZEM_B, + ids=CASES_CALC_PTF_VARALLYAI1982_CHERNOZEM_B_IDS, ) -def test_calc_ptf_varallyai1982_chernozem_b_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_varallyai1982_chernozem_b_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_varallyai1982_chernozem_b(**inputs) - assert result.theta_0 == pytest.approx(expected['theta_0'], rel=rtol, abs=atol) - assert result.m == pytest.approx(expected['m'], rel=rtol, abs=atol) - assert result.pf_star == pytest.approx(expected['pf_star'], rel=rtol, abs=atol) + assert_close( + result.theta_0, + expected['theta_0'], + absolute=0.1, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_percent', + source='registry', + ) + assert_close( + result.m, + expected['m'], + absolute=0.001, + relative=0.0, + quantity='varallyai_m', + unit='dimensionless', + source='registry', + ) + assert_close( + result.pf_star, + expected['pf_star'], + absolute=0.001, + relative=0.0, + quantity='varallyai_pf_star', + unit='dimensionless', + source='registry', + ) def test_calc_ptf_varallyai1982_chernozem_b_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_VARALLYAI1982_CHERNOZEM_B, Varallyai1982Parameters ) result = calc_ptf_varallyai1982_chernozem_b(**inputs, out=None) - assert result.theta_0[0] == pytest.approx(expected['theta_0'], rel=rtol, abs=atol) - assert result.m[0] == pytest.approx(expected['m'], rel=rtol, abs=atol) - assert result.pf_star[0] == pytest.approx(expected['pf_star'], rel=rtol, abs=atol) + assert_close( + result.theta_0[0], + expected['theta_0'], + absolute=0.1, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_percent', + source='registry', + ) + assert_close( + result.m[0], + expected['m'], + absolute=0.001, + relative=0.0, + quantity='varallyai_m', + unit='dimensionless', + source='registry', + ) + assert_close( + result.pf_star[0], + expected['pf_star'], + absolute=0.001, + relative=0.0, + quantity='varallyai_pf_star', + unit='dimensionless', + source='registry', + ) def test_calc_ptf_varallyai1982_chernozem_b_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_VARALLYAI1982_CHERNOZEM_B, Varallyai1982Parameters ) result = calc_ptf_varallyai1982_chernozem_b(**inputs, out=out) for actual, expected_out in zip(result, out, strict=True): assert actual is expected_out - assert result.theta_0[0] == pytest.approx(expected['theta_0'], rel=rtol, abs=atol) - assert result.m[0] == pytest.approx(expected['m'], rel=rtol, abs=atol) - assert result.pf_star[0] == pytest.approx(expected['pf_star'], rel=rtol, abs=atol) + assert_close( + result.theta_0[0], + expected['theta_0'], + absolute=0.1, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_percent', + source='registry', + ) + assert_close( + result.m[0], + expected['m'], + absolute=0.001, + relative=0.0, + quantity='varallyai_m', + unit='dimensionless', + source='registry', + ) + assert_close( + result.pf_star[0], + expected['pf_star'], + absolute=0.001, + relative=0.0, + quantity='varallyai_pf_star', + unit='dimensionless', + source='registry', + ) CASES_CALC_PTF_VARALLYAI1982_CHERNOZEM_C = [ ( {'bulk_density': 1.4, 'fine_fraction': 0.35}, - {'m': 0.84011, 'pf_star': 3.59772, 'theta_0': 49.87}, - 1e-12, - 1e-12, + {'theta_0': 49.87, 'm': 0.84011, 'pf_star': 3.59772}, ), ] +CASES_CALC_PTF_VARALLYAI1982_CHERNOZEM_C_IDS = [ + 'representative_review_case', +] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_VARALLYAI1982_CHERNOZEM_C + ('inputs', 'expected'), + CASES_CALC_PTF_VARALLYAI1982_CHERNOZEM_C, + ids=CASES_CALC_PTF_VARALLYAI1982_CHERNOZEM_C_IDS, ) -def test_calc_ptf_varallyai1982_chernozem_c_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_varallyai1982_chernozem_c_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_varallyai1982_chernozem_c(**inputs) - assert result.theta_0 == pytest.approx(expected['theta_0'], rel=rtol, abs=atol) - assert result.m == pytest.approx(expected['m'], rel=rtol, abs=atol) - assert result.pf_star == pytest.approx(expected['pf_star'], rel=rtol, abs=atol) + assert_close( + result.theta_0, + expected['theta_0'], + absolute=0.1, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_percent', + source='registry', + ) + assert_close( + result.m, + expected['m'], + absolute=0.001, + relative=0.0, + quantity='varallyai_m', + unit='dimensionless', + source='registry', + ) + assert_close( + result.pf_star, + expected['pf_star'], + absolute=0.001, + relative=0.0, + quantity='varallyai_pf_star', + unit='dimensionless', + source='registry', + ) def test_calc_ptf_varallyai1982_chernozem_c_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_VARALLYAI1982_CHERNOZEM_C, Varallyai1982Parameters ) result = calc_ptf_varallyai1982_chernozem_c(**inputs, out=None) - assert result.theta_0[0] == pytest.approx(expected['theta_0'], rel=rtol, abs=atol) - assert result.m[0] == pytest.approx(expected['m'], rel=rtol, abs=atol) - assert result.pf_star[0] == pytest.approx(expected['pf_star'], rel=rtol, abs=atol) + assert_close( + result.theta_0[0], + expected['theta_0'], + absolute=0.1, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_percent', + source='registry', + ) + assert_close( + result.m[0], + expected['m'], + absolute=0.001, + relative=0.0, + quantity='varallyai_m', + unit='dimensionless', + source='registry', + ) + assert_close( + result.pf_star[0], + expected['pf_star'], + absolute=0.001, + relative=0.0, + quantity='varallyai_pf_star', + unit='dimensionless', + source='registry', + ) def test_calc_ptf_varallyai1982_chernozem_c_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_VARALLYAI1982_CHERNOZEM_C, Varallyai1982Parameters ) result = calc_ptf_varallyai1982_chernozem_c(**inputs, out=out) for actual, expected_out in zip(result, out, strict=True): assert actual is expected_out - assert result.theta_0[0] == pytest.approx(expected['theta_0'], rel=rtol, abs=atol) - assert result.m[0] == pytest.approx(expected['m'], rel=rtol, abs=atol) - assert result.pf_star[0] == pytest.approx(expected['pf_star'], rel=rtol, abs=atol) + assert_close( + result.theta_0[0], + expected['theta_0'], + absolute=0.1, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_percent', + source='registry', + ) + assert_close( + result.m[0], + expected['m'], + absolute=0.001, + relative=0.0, + quantity='varallyai_m', + unit='dimensionless', + source='registry', + ) + assert_close( + result.pf_star[0], + expected['pf_star'], + absolute=0.001, + relative=0.0, + quantity='varallyai_pf_star', + unit='dimensionless', + source='registry', + ) diff --git a/targets/ptfkit-py/tests/test_vereecken1989.py b/targets/ptfkit-py/tests/test_vereecken1989.py index dec8bab..47edaad 100644 --- a/targets/ptfkit-py/tests/test_vereecken1989.py +++ b/targets/ptfkit-py/tests/test_vereecken1989.py @@ -3,7 +3,7 @@ import pytest -from _helpers import prepare_vector_case +from _helpers import assert_close, prepare_vector_case from ptfkit.vereecken1989 import ( Vereecken1989DetailedPTFResult, Vereecken1989PTFResult, @@ -16,51 +16,148 @@ ( {'bulk_density': 1.466, 'carbon': 1.03, 'clay': 10.93, 'sand': 52.14}, { - 'alpha': 0.003581613076579849, - 'n': 0.8602234826041976, 'theta_r': 0.08406999999999999, 'theta_s': 0.4060520000000001, + 'alpha': 0.003581613076579849, + 'n': 0.8602234826041976, }, - 1e-12, - 1e-12, ), ] +CASES_CALC_PTF_VEREECKEN1989_IDS = [ + 'dataset_mean_properties', +] -@pytest.mark.parametrize(('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_VEREECKEN1989) -def test_calc_ptf_vereecken1989_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float -): +@pytest.mark.parametrize( + ('inputs', 'expected'), CASES_CALC_PTF_VEREECKEN1989, ids=CASES_CALC_PTF_VEREECKEN1989_IDS +) +def test_calc_ptf_vereecken1989_verification(inputs: dict[str, float], expected: dict[str, float]): result = calc_ptf_vereecken1989(**inputs) - assert result.theta_r == pytest.approx(expected['theta_r'], rel=rtol, abs=atol) - assert result.theta_s == pytest.approx(expected['theta_s'], rel=rtol, abs=atol) - assert result.alpha == pytest.approx(expected['alpha'], rel=rtol, abs=atol) - assert result.n == pytest.approx(expected['n'], rel=rtol, abs=atol) + assert_close( + result.theta_r, + expected['theta_r'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_s, + expected['theta_s'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.alpha, + expected['alpha'], + absolute=1e-5, + relative=0.0, + quantity='van_genuchten_alpha', + unit='inverse_centimeter', + source='registry', + ) + assert_close( + result.n, + expected['n'], + absolute=0.001, + relative=0.0, + quantity='van_genuchten_n', + unit='dimensionless', + source='registry', + ) def test_calc_ptf_vereecken1989_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_VEREECKEN1989, Vereecken1989PTFResult ) result = calc_ptf_vereecken1989(**inputs, out=None) - assert result.theta_r[0] == pytest.approx(expected['theta_r'], rel=rtol, abs=atol) - assert result.theta_s[0] == pytest.approx(expected['theta_s'], rel=rtol, abs=atol) - assert result.alpha[0] == pytest.approx(expected['alpha'], rel=rtol, abs=atol) - assert result.n[0] == pytest.approx(expected['n'], rel=rtol, abs=atol) + assert_close( + result.theta_r[0], + expected['theta_r'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_s[0], + expected['theta_s'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.alpha[0], + expected['alpha'], + absolute=1e-5, + relative=0.0, + quantity='van_genuchten_alpha', + unit='inverse_centimeter', + source='registry', + ) + assert_close( + result.n[0], + expected['n'], + absolute=0.001, + relative=0.0, + quantity='van_genuchten_n', + unit='dimensionless', + source='registry', + ) def test_calc_ptf_vereecken1989_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_VEREECKEN1989, Vereecken1989PTFResult ) result = calc_ptf_vereecken1989(**inputs, out=out) for actual, expected_out in zip(result, out, strict=True): assert actual is expected_out - assert result.theta_r[0] == pytest.approx(expected['theta_r'], rel=rtol, abs=atol) - assert result.theta_s[0] == pytest.approx(expected['theta_s'], rel=rtol, abs=atol) - assert result.alpha[0] == pytest.approx(expected['alpha'], rel=rtol, abs=atol) - assert result.n[0] == pytest.approx(expected['n'], rel=rtol, abs=atol) + assert_close( + result.theta_r[0], + expected['theta_r'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_s[0], + expected['theta_s'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.alpha[0], + expected['alpha'], + absolute=1e-5, + relative=0.0, + quantity='van_genuchten_alpha', + unit='inverse_centimeter', + source='registry', + ) + assert_close( + result.n[0], + expected['n'], + absolute=0.001, + relative=0.0, + quantity='van_genuchten_n', + unit='dimensionless', + source='registry', + ) CASES_CALC_PTF_VEREECKEN1989_DETAILED = [ @@ -81,50 +178,149 @@ def test_calc_ptf_vereecken1989_out(): 'particle_50_20': 24.83, }, { - 'alpha': 0.02522115884641546, - 'n': 0.517175605329397, 'theta_r': 0.08277820000000001, 'theta_s': 0.4060520000000001, + 'alpha': 0.02522115884641546, + 'n': 0.517175605329397, }, - 1e-12, - 1e-12, ), ] +CASES_CALC_PTF_VEREECKEN1989_DETAILED_IDS = [ + 'dataset_mean_properties', +] @pytest.mark.parametrize( - ('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_VEREECKEN1989_DETAILED + ('inputs', 'expected'), + CASES_CALC_PTF_VEREECKEN1989_DETAILED, + ids=CASES_CALC_PTF_VEREECKEN1989_DETAILED_IDS, ) -def test_calc_ptf_vereecken1989_detailed_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float +def test_calc_ptf_vereecken1989_detailed_verification( + inputs: dict[str, float], expected: dict[str, float] ): result = calc_ptf_vereecken1989_detailed(**inputs) - assert result.theta_r == pytest.approx(expected['theta_r'], rel=rtol, abs=atol) - assert result.theta_s == pytest.approx(expected['theta_s'], rel=rtol, abs=atol) - assert result.alpha == pytest.approx(expected['alpha'], rel=rtol, abs=atol) - assert result.n == pytest.approx(expected['n'], rel=rtol, abs=atol) + assert_close( + result.theta_r, + expected['theta_r'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_s, + expected['theta_s'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.alpha, + expected['alpha'], + absolute=1e-5, + relative=0.0, + quantity='van_genuchten_alpha', + unit='inverse_centimeter', + source='registry', + ) + assert_close( + result.n, + expected['n'], + absolute=0.001, + relative=0.0, + quantity='van_genuchten_n', + unit='dimensionless', + source='registry', + ) def test_calc_ptf_vereecken1989_detailed_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( + inputs, expected, _out = prepare_vector_case( CASES_CALC_PTF_VEREECKEN1989_DETAILED, Vereecken1989DetailedPTFResult ) result = calc_ptf_vereecken1989_detailed(**inputs, out=None) - assert result.theta_r[0] == pytest.approx(expected['theta_r'], rel=rtol, abs=atol) - assert result.theta_s[0] == pytest.approx(expected['theta_s'], rel=rtol, abs=atol) - assert result.alpha[0] == pytest.approx(expected['alpha'], rel=rtol, abs=atol) - assert result.n[0] == pytest.approx(expected['n'], rel=rtol, abs=atol) + assert_close( + result.theta_r[0], + expected['theta_r'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_s[0], + expected['theta_s'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.alpha[0], + expected['alpha'], + absolute=1e-5, + relative=0.0, + quantity='van_genuchten_alpha', + unit='inverse_centimeter', + source='registry', + ) + assert_close( + result.n[0], + expected['n'], + absolute=0.001, + relative=0.0, + quantity='van_genuchten_n', + unit='dimensionless', + source='registry', + ) def test_calc_ptf_vereecken1989_detailed_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( + inputs, expected, out = prepare_vector_case( CASES_CALC_PTF_VEREECKEN1989_DETAILED, Vereecken1989DetailedPTFResult ) result = calc_ptf_vereecken1989_detailed(**inputs, out=out) for actual, expected_out in zip(result, out, strict=True): assert actual is expected_out - assert result.theta_r[0] == pytest.approx(expected['theta_r'], rel=rtol, abs=atol) - assert result.theta_s[0] == pytest.approx(expected['theta_s'], rel=rtol, abs=atol) - assert result.alpha[0] == pytest.approx(expected['alpha'], rel=rtol, abs=atol) - assert result.n[0] == pytest.approx(expected['n'], rel=rtol, abs=atol) + assert_close( + result.theta_r[0], + expected['theta_r'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_s[0], + expected['theta_s'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.alpha[0], + expected['alpha'], + absolute=1e-5, + relative=0.0, + quantity='van_genuchten_alpha', + unit='inverse_centimeter', + source='registry', + ) + assert_close( + result.n[0], + expected['n'], + absolute=0.001, + relative=0.0, + quantity='van_genuchten_n', + unit='dimensionless', + source='registry', + ) diff --git a/targets/ptfkit-py/tests/test_wang2012.py b/targets/ptfkit-py/tests/test_wang2012.py index 8f7be0e..a7f7918 100644 --- a/targets/ptfkit-py/tests/test_wang2012.py +++ b/targets/ptfkit-py/tests/test_wang2012.py @@ -3,7 +3,7 @@ import pytest -from _helpers import prepare_vector_case +from _helpers import assert_close, prepare_vector_case from ptfkit.wang2012 import Wang2012PTFResult, calc_ptf_wang2012 @@ -17,41 +17,110 @@ 'silt': 10.0, 'soil_organic_carbon': 0.033, }, - {'k_sat': 3.872974e-5, 'theta_fc': 0.38491949, 'theta_s': 0.61540575}, - 1e-6, - 1e-12, + {'theta_s': 0.61540575, 'theta_fc': 0.38491949, 'k_sat': 3.872974e-5}, ), ] +CASES_CALC_PTF_WANG2012_IDS = [ + 'derivation_minimum_soc_case', +] -@pytest.mark.parametrize(('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_WANG2012) -def test_calc_ptf_wang2012_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float -): +@pytest.mark.parametrize( + ('inputs', 'expected'), CASES_CALC_PTF_WANG2012, ids=CASES_CALC_PTF_WANG2012_IDS +) +def test_calc_ptf_wang2012_verification(inputs: dict[str, float], expected: dict[str, float]): result = calc_ptf_wang2012(**inputs) - assert result.theta_s == pytest.approx(expected['theta_s'], rel=rtol, abs=atol) - assert result.theta_fc == pytest.approx(expected['theta_fc'], rel=rtol, abs=atol) - assert result.k_sat == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result.theta_s, + expected['theta_s'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_fc, + expected['theta_fc'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.k_sat, + expected['k_sat'], + absolute=1e-10, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='meter_per_second', + source='registry', + ) def test_calc_ptf_wang2012_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( - CASES_CALC_PTF_WANG2012, Wang2012PTFResult - ) + inputs, expected, _out = prepare_vector_case(CASES_CALC_PTF_WANG2012, Wang2012PTFResult) result = calc_ptf_wang2012(**inputs, out=None) - assert result.theta_s[0] == pytest.approx(expected['theta_s'], rel=rtol, abs=atol) - assert result.theta_fc[0] == pytest.approx(expected['theta_fc'], rel=rtol, abs=atol) - assert result.k_sat[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result.theta_s[0], + expected['theta_s'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_fc[0], + expected['theta_fc'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.k_sat[0], + expected['k_sat'], + absolute=1e-10, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='meter_per_second', + source='registry', + ) def test_calc_ptf_wang2012_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( - CASES_CALC_PTF_WANG2012, Wang2012PTFResult - ) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_WANG2012, Wang2012PTFResult) result = calc_ptf_wang2012(**inputs, out=out) for actual, expected_out in zip(result, out, strict=True): assert actual is expected_out - assert result.theta_s[0] == pytest.approx(expected['theta_s'], rel=rtol, abs=atol) - assert result.theta_fc[0] == pytest.approx(expected['theta_fc'], rel=rtol, abs=atol) - assert result.k_sat[0] == pytest.approx(expected['k_sat'], rel=rtol, abs=atol) + assert_close( + result.theta_s[0], + expected['theta_s'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_fc[0], + expected['theta_fc'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.k_sat[0], + expected['k_sat'], + absolute=1e-10, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='meter_per_second', + source='registry', + ) diff --git a/targets/ptfkit-py/tests/test_weber2020.py b/targets/ptfkit-py/tests/test_weber2020.py index fff31c2..6db484b 100644 --- a/targets/ptfkit-py/tests/test_weber2020.py +++ b/targets/ptfkit-py/tests/test_weber2020.py @@ -3,7 +3,7 @@ import pytest -from _helpers import prepare_vector_case +from _helpers import assert_close, prepare_vector_case from ptfkit.weber2020 import Weber2020PTFResult, calc_ptf_weber2020 @@ -18,60 +18,225 @@ 'theta_s_vgm': 0.45, }, { + 'theta_snc_bw': 0.06267, + 'theta_sc_bw': 0.38607, 'alpha_bw': 0.0201472407335197, - 'k_sc_bw': 172.186857498601, - 'k_snc_bw': 0.0190546071796325, 'n_bw': 1.71980542683289, 'tau_bw': -0.887, - 'theta_sc_bw': 0.38607, - 'theta_snc_bw': 0.06267, + 'k_sc_bw': 172.186857498601, + 'k_snc_bw': 0.0190546071796325, }, - 1e-12, - 1e-12, ), ] +CASES_CALC_PTF_WEBER2020_IDS = [ + 'representative_vgm_parameters', +] -@pytest.mark.parametrize(('inputs', 'expected', 'rtol', 'atol'), CASES_CALC_PTF_WEBER2020) -def test_calc_ptf_weber2020_golden( - inputs: dict[str, float], expected: dict[str, float], rtol: float, atol: float -): +@pytest.mark.parametrize( + ('inputs', 'expected'), CASES_CALC_PTF_WEBER2020, ids=CASES_CALC_PTF_WEBER2020_IDS +) +def test_calc_ptf_weber2020_verification(inputs: dict[str, float], expected: dict[str, float]): result = calc_ptf_weber2020(**inputs) - assert result.theta_snc_bw == pytest.approx(expected['theta_snc_bw'], rel=rtol, abs=atol) - assert result.theta_sc_bw == pytest.approx(expected['theta_sc_bw'], rel=rtol, abs=atol) - assert result.alpha_bw == pytest.approx(expected['alpha_bw'], rel=rtol, abs=atol) - assert result.n_bw == pytest.approx(expected['n_bw'], rel=rtol, abs=atol) - assert result.tau_bw == pytest.approx(expected['tau_bw'], rel=rtol, abs=atol) - assert result.k_sc_bw == pytest.approx(expected['k_sc_bw'], rel=rtol, abs=atol) - assert result.k_snc_bw == pytest.approx(expected['k_snc_bw'], rel=rtol, abs=atol) + assert_close( + result.theta_snc_bw, + expected['theta_snc_bw'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_sc_bw, + expected['theta_sc_bw'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.alpha_bw, + expected['alpha_bw'], + absolute=1e-5, + relative=0.0, + quantity='van_genuchten_alpha', + unit='inverse_centimeter', + source='registry', + ) + assert_close( + result.n_bw, + expected['n_bw'], + absolute=0.001, + relative=0.0, + quantity='brunswick_n', + unit='dimensionless', + source='registry', + ) + assert_close( + result.tau_bw, + expected['tau_bw'], + absolute=0.001, + relative=0.0, + quantity='brunswick_tau', + unit='dimensionless', + source='registry', + ) + assert_close( + result.k_sc_bw, + expected['k_sc_bw'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='centimeter_per_day', + source='registry', + ) + assert_close( + result.k_snc_bw, + expected['k_snc_bw'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='centimeter_per_day', + source='registry', + ) def test_calc_ptf_weber2020_array(): - inputs, expected, rtol, atol, _out = prepare_vector_case( - CASES_CALC_PTF_WEBER2020, Weber2020PTFResult - ) + inputs, expected, _out = prepare_vector_case(CASES_CALC_PTF_WEBER2020, Weber2020PTFResult) result = calc_ptf_weber2020(**inputs, out=None) - assert result.theta_snc_bw[0] == pytest.approx(expected['theta_snc_bw'], rel=rtol, abs=atol) - assert result.theta_sc_bw[0] == pytest.approx(expected['theta_sc_bw'], rel=rtol, abs=atol) - assert result.alpha_bw[0] == pytest.approx(expected['alpha_bw'], rel=rtol, abs=atol) - assert result.n_bw[0] == pytest.approx(expected['n_bw'], rel=rtol, abs=atol) - assert result.tau_bw[0] == pytest.approx(expected['tau_bw'], rel=rtol, abs=atol) - assert result.k_sc_bw[0] == pytest.approx(expected['k_sc_bw'], rel=rtol, abs=atol) - assert result.k_snc_bw[0] == pytest.approx(expected['k_snc_bw'], rel=rtol, abs=atol) + assert_close( + result.theta_snc_bw[0], + expected['theta_snc_bw'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_sc_bw[0], + expected['theta_sc_bw'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.alpha_bw[0], + expected['alpha_bw'], + absolute=1e-5, + relative=0.0, + quantity='van_genuchten_alpha', + unit='inverse_centimeter', + source='registry', + ) + assert_close( + result.n_bw[0], + expected['n_bw'], + absolute=0.001, + relative=0.0, + quantity='brunswick_n', + unit='dimensionless', + source='registry', + ) + assert_close( + result.tau_bw[0], + expected['tau_bw'], + absolute=0.001, + relative=0.0, + quantity='brunswick_tau', + unit='dimensionless', + source='registry', + ) + assert_close( + result.k_sc_bw[0], + expected['k_sc_bw'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='centimeter_per_day', + source='registry', + ) + assert_close( + result.k_snc_bw[0], + expected['k_snc_bw'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='centimeter_per_day', + source='registry', + ) def test_calc_ptf_weber2020_out(): - inputs, expected, rtol, atol, out = prepare_vector_case( - CASES_CALC_PTF_WEBER2020, Weber2020PTFResult - ) + inputs, expected, out = prepare_vector_case(CASES_CALC_PTF_WEBER2020, Weber2020PTFResult) result = calc_ptf_weber2020(**inputs, out=out) for actual, expected_out in zip(result, out, strict=True): assert actual is expected_out - assert result.theta_snc_bw[0] == pytest.approx(expected['theta_snc_bw'], rel=rtol, abs=atol) - assert result.theta_sc_bw[0] == pytest.approx(expected['theta_sc_bw'], rel=rtol, abs=atol) - assert result.alpha_bw[0] == pytest.approx(expected['alpha_bw'], rel=rtol, abs=atol) - assert result.n_bw[0] == pytest.approx(expected['n_bw'], rel=rtol, abs=atol) - assert result.tau_bw[0] == pytest.approx(expected['tau_bw'], rel=rtol, abs=atol) - assert result.k_sc_bw[0] == pytest.approx(expected['k_sc_bw'], rel=rtol, abs=atol) - assert result.k_snc_bw[0] == pytest.approx(expected['k_snc_bw'], rel=rtol, abs=atol) + assert_close( + result.theta_snc_bw[0], + expected['theta_snc_bw'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.theta_sc_bw[0], + expected['theta_sc_bw'], + absolute=0.001, + relative=0.0, + quantity='volumetric_water_content', + unit='volume_fraction', + source='registry', + ) + assert_close( + result.alpha_bw[0], + expected['alpha_bw'], + absolute=1e-5, + relative=0.0, + quantity='van_genuchten_alpha', + unit='inverse_centimeter', + source='registry', + ) + assert_close( + result.n_bw[0], + expected['n_bw'], + absolute=0.001, + relative=0.0, + quantity='brunswick_n', + unit='dimensionless', + source='registry', + ) + assert_close( + result.tau_bw[0], + expected['tau_bw'], + absolute=0.001, + relative=0.0, + quantity='brunswick_tau', + unit='dimensionless', + source='registry', + ) + assert_close( + result.k_sc_bw[0], + expected['k_sc_bw'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='centimeter_per_day', + source='registry', + ) + assert_close( + result.k_snc_bw[0], + expected['k_snc_bw'], + absolute=0.0001, + relative=0.01, + quantity='saturated_hydraulic_conductivity', + unit='centimeter_per_day', + source='registry', + ) diff --git a/targets/ptfkit-rs/src/ahuja1984.rs b/targets/ptfkit-rs/src/ahuja1984.rs index f3df162..e4969c0 100644 --- a/targets/ptfkit-rs/src/ahuja1984.rs +++ b/targets/ptfkit-rs/src/ahuja1984.rs @@ -18,6 +18,41 @@ Undisturbed cores from 15 sites in a 9.6-ha Renfro silt loam watershed and 54 co related Hawaii Oxic soils; the Hawaii study also supplied 35 field measurements of saturated hydraulic conductivity."] +#[cfg(test)] +fn resolved_tolerance(expected: f64, absolute: f64, relative: f64) -> f64 { + absolute + .max(relative * expected.abs()) + .max(0.00000000000001f64) +} +#[cfg(test)] +fn assert_close( + actual: f64, + expected: f64, + absolute: f64, + relative: f64, + quantity: &str, + unit: &str, + source: &str, +) { + let difference = (actual - expected).abs(); + let tolerance = resolved_tolerance(expected, absolute, relative); + assert!( + difference <= tolerance, + "actual={actual}, expected={expected}, difference={difference}, tolerance={tolerance}, quantity={quantity}, unit={unit}, source={source}" + ); +} +#[cfg(test)] +mod comparator_tests { + use super::*; + #[test] + fn accepts_below_and_rejects_above_tolerance() { + for expected in [0.0, 2.0, -2.0] { + let tolerance = resolved_tolerance(expected, 0.001, 0.01); + assert!((expected + tolerance * 0.5 - expected).abs() <= tolerance); + assert!((expected + tolerance * 2.0 - expected).abs() > tolerance); + } + } +} #[doc = r"Estimate saturated hydraulic conductivity from total porosity and water content at -33 kPa using user-supplied empirical coefficients. @@ -73,20 +108,30 @@ pub fn calc_ptf_ahuja1984( #[cfg(test)] mod tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn exponent_four() { let result = calc_ptf_ahuja1984(0.45f64, 0.25f64, 100f64, 4f64); - assert_close(result, 0.16f64, 0.00000000000001f64, 0.000000000001f64); + assert_close( + result, + 0.16f64, + 0.00001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "centimeter_per_hour", + "registry", + ); } #[test] fn exponent_five() { let result = calc_ptf_ahuja1984(0.5f64, 0.2f64, 100f64, 5f64); - assert_close(result, 0.243f64, 0.00000000000001f64, 0.000000000001f64); + assert_close( + result, + 0.243f64, + 0.00001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "centimeter_per_hour", + "registry", + ); } } diff --git a/targets/ptfkit-rs/src/aimrun2009.rs b/targets/ptfkit-rs/src/aimrun2009.rs index f06086b..d37e949 100644 --- a/targets/ptfkit-rs/src/aimrun2009.rs +++ b/targets/ptfkit-rs/src/aimrun2009.rs @@ -17,6 +17,41 @@ Agricultural Development Area (IADA Barat Laut Selangor), Malaysia 408 lowland paddy soil samples from Sawah Sempadan rice cultivation area."] +#[cfg(test)] +fn resolved_tolerance(expected: f64, absolute: f64, relative: f64) -> f64 { + absolute + .max(relative * expected.abs()) + .max(0.00000000000001f64) +} +#[cfg(test)] +fn assert_close( + actual: f64, + expected: f64, + absolute: f64, + relative: f64, + quantity: &str, + unit: &str, + source: &str, +) { + let difference = (actual - expected).abs(); + let tolerance = resolved_tolerance(expected, absolute, relative); + assert!( + difference <= tolerance, + "actual={actual}, expected={expected}, difference={difference}, tolerance={tolerance}, quantity={quantity}, unit={unit}, source={source}" + ); +} +#[cfg(test)] +mod comparator_tests { + use super::*; + #[test] + fn accepts_below_and_rejects_above_tolerance() { + for expected in [0.0, 2.0, -2.0] { + let tolerance = resolved_tolerance(expected, 0.001, 0.01); + assert!((expected + tolerance * 0.5 - expected).abs() <= tolerance); + assert!((expected + tolerance * 2.0 - expected).abs() > tolerance); + } + } +} #[doc = r"Estimate saturated hydraulic conductivity for lowland paddy soils. # Arguments @@ -58,20 +93,17 @@ pub fn calc_ptf_aimrun2009(clay: f64, bulk_density: f64, organic_matter: f64, gm #[cfg(test)] mod tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn mean_topsoil_layer() { let result = calc_ptf_aimrun2009(43.88f64, 0.94f64, 12.07f64, 0.01f64); assert_close( result, 0.00000007358406556179513f64, - 0.000000000001f64, - 0.00000001f64, + 0.0000000001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "meter_per_second", + "registry", ); } #[test] @@ -80,8 +112,11 @@ mod tests { assert_close( result, 0.0000000307872446717209f64, - 0.000000000001f64, - 0.00000001f64, + 0.0000000001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "meter_per_second", + "registry", ); } #[test] @@ -90,8 +125,11 @@ mod tests { assert_close( result, 0.000000023343051908963327f64, - 0.000000000001f64, - 0.00000001f64, + 0.0000000001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "meter_per_second", + "registry", ); } #[test] @@ -100,8 +138,11 @@ mod tests { assert_close( result, 0.00000003831168764444974f64, - 0.000000000001f64, - 0.00000001f64, + 0.0000000001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "meter_per_second", + "registry", ); } } diff --git a/targets/ptfkit-rs/src/beniaich2023.rs b/targets/ptfkit-rs/src/beniaich2023.rs index 80f00fd..4548a9d 100644 --- a/targets/ptfkit-rs/src/beniaich2023.rs +++ b/targets/ptfkit-rs/src/beniaich2023.rs @@ -19,6 +19,41 @@ Agricultural topsoils in Doukkala, Gharb-Loukouss, Moulouya, and Tadla, Morocco 331 disturbed topsoil samples collected at 0-20 cm from 2019 to 2022; random 50% calibration and 50% validation subsets."] +#[cfg(test)] +fn resolved_tolerance(expected: f64, absolute: f64, relative: f64) -> f64 { + absolute + .max(relative * expected.abs()) + .max(0.00000000000001f64) +} +#[cfg(test)] +fn assert_close( + actual: f64, + expected: f64, + absolute: f64, + relative: f64, + quantity: &str, + unit: &str, + source: &str, +) { + let difference = (actual - expected).abs(); + let tolerance = resolved_tolerance(expected, absolute, relative); + assert!( + difference <= tolerance, + "actual={actual}, expected={expected}, difference={difference}, tolerance={tolerance}, quantity={quantity}, unit={unit}, source={source}" + ); +} +#[cfg(test)] +mod comparator_tests { + use super::*; + #[test] + fn accepts_below_and_rejects_above_tolerance() { + for expected in [0.0, 2.0, -2.0] { + let tolerance = resolved_tolerance(expected, 0.001, 0.01); + assert!((expected + tolerance * 0.5 - expected).abs() <= tolerance); + assert!((expected + tolerance * 2.0 - expected).abs() > tolerance); + } + } +} #[doc = r"Results returned by `calc_ptf_beniaich2023_slr1`."] #[derive(Clone, Copy, Debug, PartialEq)] pub struct Beniaich2023PTFResult { @@ -67,32 +102,35 @@ pub fn calc_ptf_beniaich2023_slr1(clay: f64) -> Beniaich2023PTFResult { #[cfg(test)] mod calc_ptf_beniaich2023_slr1_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn representative_case() { let result = calc_ptf_beniaich2023_slr1(20f64); assert_close( result.water_saturation, 0.57427f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); assert_close( result.water_field_capacity, 0.17577f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); assert_close( result.water_wilting_point, 0.09621f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); } } @@ -134,32 +172,35 @@ pub fn calc_ptf_beniaich2023_slr2(silt: f64) -> Beniaich2023PTFResult { #[cfg(test)] mod calc_ptf_beniaich2023_slr2_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn representative_case() { let result = calc_ptf_beniaich2023_slr2(30f64); assert_close( result.water_saturation, 0.68478f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); assert_close( result.water_field_capacity, 0.24878f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); assert_close( result.water_wilting_point, 0.16131f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); } } @@ -201,32 +242,35 @@ pub fn calc_ptf_beniaich2023_slr3(sand: f64) -> Beniaich2023PTFResult { #[cfg(test)] mod calc_ptf_beniaich2023_slr3_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn representative_case() { let result = calc_ptf_beniaich2023_slr3(50f64); assert_close( result.water_saturation, 0.6007f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); assert_close( result.water_field_capacity, 0.1848f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); assert_close( result.water_wilting_point, 0.11077f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); } } @@ -270,32 +314,35 @@ pub fn calc_ptf_beniaich2023_slr4(clay: f64, silt: f64) -> Beniaich2023PTFResult #[cfg(test)] mod calc_ptf_beniaich2023_slr4_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn representative_case() { let result = calc_ptf_beniaich2023_slr4(20f64, 30f64); assert_close( result.water_saturation, 0.74501f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); assert_close( result.water_field_capacity, 0.30678f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); assert_close( result.water_wilting_point, 0.19915f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); } } @@ -339,32 +386,35 @@ pub fn calc_ptf_beniaich2023_slr5(clay: f64, silt: f64) -> Beniaich2023PTFResult #[cfg(test)] mod calc_ptf_beniaich2023_slr5_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn representative_case() { let result = calc_ptf_beniaich2023_slr5(20f64, 40f64); assert_close( result.water_saturation, 0.68578f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); assert_close( result.water_field_capacity, 0.241875f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); assert_close( result.water_wilting_point, 0.16176f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); } } @@ -406,32 +456,35 @@ pub fn calc_ptf_beniaich2023_slr6(soil_organic_matter: f64) -> Beniaich2023PTFRe #[cfg(test)] mod calc_ptf_beniaich2023_slr6_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn representative_case() { let result = calc_ptf_beniaich2023_slr6(2f64); assert_close( result.water_saturation, 0.66749f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); assert_close( result.water_field_capacity, 0.24009f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); assert_close( result.water_wilting_point, 0.15562f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); } } @@ -482,32 +535,35 @@ pub fn calc_ptf_beniaich2023_mlr1( #[cfg(test)] mod calc_ptf_beniaich2023_mlr1_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn representative_case() { let result = calc_ptf_beniaich2023_mlr1(30f64, 50f64, 2f64); assert_close( result.water_saturation, 0.56266f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); assert_close( result.water_field_capacity, 0.17238f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); assert_close( result.water_wilting_point, 0.09366f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); } } @@ -553,32 +609,35 @@ pub fn calc_ptf_beniaich2023_mlr2(sand: f64, soil_organic_matter: f64) -> Beniai #[cfg(test)] mod calc_ptf_beniaich2023_mlr2_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn representative_case() { let result = calc_ptf_beniaich2023_mlr2(50f64, 2f64); assert_close( result.water_saturation, 0.58954f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); assert_close( result.water_field_capacity, 0.18025f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); assert_close( result.water_wilting_point, 0.10825f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); } } @@ -624,32 +683,35 @@ pub fn calc_ptf_beniaich2023_mlr3(silt: f64, soil_organic_matter: f64) -> Beniai #[cfg(test)] mod calc_ptf_beniaich2023_mlr3_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn representative_case() { let result = calc_ptf_beniaich2023_mlr3(30f64, 2f64); assert_close( result.water_saturation, 0.67031f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); assert_close( result.water_field_capacity, 0.24275f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); assert_close( result.water_wilting_point, 0.15755f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); } } @@ -695,32 +757,35 @@ pub fn calc_ptf_beniaich2023_mlr4(clay: f64, soil_organic_matter: f64) -> Beniai #[cfg(test)] mod calc_ptf_beniaich2023_mlr4_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn representative_case() { let result = calc_ptf_beniaich2023_mlr4(20f64, 2f64); assert_close( result.water_saturation, 0.5589f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); assert_close( result.water_field_capacity, 0.16859f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); assert_close( result.water_wilting_point, 0.09157f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); } } @@ -771,32 +836,35 @@ pub fn calc_ptf_beniaich2023_mlr5( #[cfg(test)] mod calc_ptf_beniaich2023_mlr5_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn representative_case() { let result = calc_ptf_beniaich2023_mlr5(20f64, 30f64, 2f64); assert_close( result.water_saturation, 0.56229f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); assert_close( result.water_field_capacity, 0.172f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); assert_close( result.water_wilting_point, 0.09379f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); } } diff --git a/targets/ptfkit-rs/src/chakraborty2011.rs b/targets/ptfkit-rs/src/chakraborty2011.rs index f173cf5..c7519a5 100644 --- a/targets/ptfkit-rs/src/chakraborty2011.rs +++ b/targets/ptfkit-rs/src/chakraborty2011.rs @@ -18,6 +18,41 @@ India for regression development and 80 independent samples for validation. Analyses were performed during 2006-2008."] +#[cfg(test)] +fn resolved_tolerance(expected: f64, absolute: f64, relative: f64) -> f64 { + absolute + .max(relative * expected.abs()) + .max(0.00000000000001f64) +} +#[cfg(test)] +fn assert_close( + actual: f64, + expected: f64, + absolute: f64, + relative: f64, + quantity: &str, + unit: &str, + source: &str, +) { + let difference = (actual - expected).abs(); + let tolerance = resolved_tolerance(expected, absolute, relative); + assert!( + difference <= tolerance, + "actual={actual}, expected={expected}, difference={difference}, tolerance={tolerance}, quantity={quantity}, unit={unit}, source={source}" + ); +} +#[cfg(test)] +mod comparator_tests { + use super::*; + #[test] + fn accepts_below_and_rejects_above_tolerance() { + for expected in [0.0, 2.0, -2.0] { + let tolerance = resolved_tolerance(expected, 0.001, 0.01); + assert!((expected + tolerance * 0.5 - expected).abs() <= tolerance); + assert!((expected + tolerance * 2.0 - expected).abs() > tolerance); + } + } +} #[doc = r"Results returned by `calc_ptf_chakraborty2011_eq1`."] #[derive(Clone, Copy, Debug, PartialEq)] pub struct Chakraborty2011PTFResult { @@ -73,38 +108,44 @@ pub fn calc_ptf_chakraborty2011_eq1(clay: f64, silt: f64) -> Chakraborty2011PTFR #[cfg(test)] mod calc_ptf_chakraborty2011_eq1_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn representative_case() { let result = calc_ptf_chakraborty2011_eq1(20f64, 30f64); assert_close( result.water_content_33, 0.2488f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); assert_close( result.water_content_100, 0.19758f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); assert_close( result.water_content_500, 0.13528f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); assert_close( result.water_content_1500, 0.11702f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); } } @@ -151,38 +192,44 @@ pub fn calc_ptf_chakraborty2011_eq2(sand: f64, bulk_density: f64) -> Chakraborty #[cfg(test)] mod calc_ptf_chakraborty2011_eq2_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn representative_case() { let result = calc_ptf_chakraborty2011_eq2(50f64, 1.5f64); assert_close( result.water_content_33, 0.219415f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); assert_close( result.water_content_100, 0.175765f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); assert_close( result.water_content_500, 0.141025f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); assert_close( result.water_content_1500, 0.118315f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); } } @@ -238,38 +285,44 @@ pub fn calc_ptf_chakraborty2011_eq3( #[cfg(test)] mod calc_ptf_chakraborty2011_eq3_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn representative_case() { let result = calc_ptf_chakraborty2011_eq3(20f64, 30f64, 1.5f64); assert_close( result.water_content_33, 0.25776f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); assert_close( result.water_content_100, 0.201085f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); assert_close( result.water_content_500, 0.139175f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); assert_close( result.water_content_1500, 0.117865f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); } } @@ -321,38 +374,44 @@ pub fn calc_ptf_chakraborty2011_eq4(clay: f64, silt: f64, sand: f64) -> Chakrabo #[cfg(test)] mod calc_ptf_chakraborty2011_eq4_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn representative_case() { let result = calc_ptf_chakraborty2011_eq4(20f64, 30f64, 50f64); assert_close( result.water_content_33, 0.24397f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); assert_close( result.water_content_100, 0.19235f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); assert_close( result.water_content_500, 0.13116f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); assert_close( result.water_content_1500, 0.11325f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); } } @@ -414,38 +473,44 @@ pub fn calc_ptf_chakraborty2011_eq5( #[cfg(test)] mod calc_ptf_chakraborty2011_eq5_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn representative_case() { let result = calc_ptf_chakraborty2011_eq5(20f64, 30f64, 50f64, 1.5f64); assert_close( result.water_content_33, 0.249785f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); assert_close( result.water_content_100, 0.19166f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); assert_close( result.water_content_500, 0.131065f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); assert_close( result.water_content_1500, 0.110595f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); } } @@ -517,38 +582,44 @@ pub fn calc_ptf_chakraborty2011_eq6( #[cfg(test)] mod calc_ptf_chakraborty2011_eq6_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn representative_case() { let result = calc_ptf_chakraborty2011_eq6(20f64, 30f64, 50f64, 0.5f64, 1.5f64); assert_close( result.water_content_33, 0.25207f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); assert_close( result.water_content_100, 0.19695f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); assert_close( result.water_content_500, 0.13364f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); assert_close( result.water_content_1500, 0.113175f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravimetric_water_content", + "mass_fraction", + "registry", ); } } diff --git a/targets/ptfkit-rs/src/clapp1978.rs b/targets/ptfkit-rs/src/clapp1978.rs index 9229840..1eb58ae 100644 --- a/targets/ptfkit-rs/src/clapp1978.rs +++ b/targets/ptfkit-rs/src/clapp1978.rs @@ -136,6 +136,41 @@ impl From for Clapp1978Parameters { } } } +#[cfg(test)] +fn resolved_tolerance(expected: f64, absolute: f64, relative: f64) -> f64 { + absolute + .max(relative * expected.abs()) + .max(0.00000000000001f64) +} +#[cfg(test)] +fn assert_close( + actual: f64, + expected: f64, + absolute: f64, + relative: f64, + quantity: &str, + unit: &str, + source: &str, +) { + let difference = (actual - expected).abs(); + let tolerance = resolved_tolerance(expected, absolute, relative); + assert!( + difference <= tolerance, + "actual={actual}, expected={expected}, difference={difference}, tolerance={tolerance}, quantity={quantity}, unit={unit}, source={source}" + ); +} +#[cfg(test)] +mod comparator_tests { + use super::*; + #[test] + fn accepts_below_and_rejects_above_tolerance() { + for expected in [0.0, 2.0, -2.0] { + let tolerance = resolved_tolerance(expected, 0.001, 0.01); + assert!((expected + tolerance * 0.5 - expected).abs() <= tolerance); + assert!((expected + tolerance * 2.0 - expected).abs() > tolerance); + } + } +} #[doc = r"Results returned by `calc_ptf_clapp1978`."] #[derive(Clone, Copy, Debug, PartialEq)] pub struct Clapp1978Parameters { @@ -194,175 +229,642 @@ pub fn calc_ptf_clapp1978(soil_texture: UsdaTextureClass) -> Clapp1978Parameters #[cfg(test)] mod tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn table_2_sand() { let result = calc_ptf_clapp1978(UsdaTextureClass::Sand); - assert_close(result.b, 4.05f64, 0f64, 0f64); - assert_close(result.saturation_suction, 3.5f64, 0f64, 0f64); - assert_close(result.wetting_front_suction, 4.66f64, 0f64, 0f64); - assert_close(result.saturated_water_content, 0.395f64, 0f64, 0f64); assert_close( - result.saturated_hydraulic_conductivity, - 1.056f64, + result.b, + 4.05f64, + 0.001f64, + 0f64, + "campbell_b", + "dimensionless", + "registry", + ); + assert_close( + result.saturation_suction, + 3.5f64, + 0.01f64, + 0f64, + "matric_potential", + "centimeter_water_head", + "registry", + ); + assert_close( + result.wetting_front_suction, + 4.66f64, + 0.01f64, 0f64, + "matric_potential", + "centimeter_water_head", + "registry", + ); + assert_close( + result.saturated_water_content, + 0.395f64, + 0.001f64, 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", + ); + assert_close( + result.saturated_hydraulic_conductivity, + 1.056f64, + 0.00001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "centimeter_per_minute", + "registry", + ); + assert_close( + result.sorptivity, + 1.52f64, + 0.0001f64, + 0.01f64, + "sorptivity", + "centimeter_per_square_root_minute", + "registry", ); - assert_close(result.sorptivity, 1.52f64, 0f64, 0f64); } #[test] fn table_2_loamy_sand() { let result = calc_ptf_clapp1978(UsdaTextureClass::LoamySand); - assert_close(result.b, 4.38f64, 0f64, 0f64); - assert_close(result.saturation_suction, 1.78f64, 0f64, 0f64); - assert_close(result.wetting_front_suction, 2.38f64, 0f64, 0f64); - assert_close(result.saturated_water_content, 0.41f64, 0f64, 0f64); assert_close( - result.saturated_hydraulic_conductivity, - 0.938f64, + result.b, + 4.38f64, + 0.001f64, 0f64, + "campbell_b", + "dimensionless", + "registry", + ); + assert_close( + result.saturation_suction, + 1.78f64, + 0.01f64, + 0f64, + "matric_potential", + "centimeter_water_head", + "registry", + ); + assert_close( + result.wetting_front_suction, + 2.38f64, + 0.01f64, 0f64, + "matric_potential", + "centimeter_water_head", + "registry", + ); + assert_close( + result.saturated_water_content, + 0.41f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", + ); + assert_close( + result.saturated_hydraulic_conductivity, + 0.938f64, + 0.00001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "centimeter_per_minute", + "registry", + ); + assert_close( + result.sorptivity, + 1.04f64, + 0.0001f64, + 0.01f64, + "sorptivity", + "centimeter_per_square_root_minute", + "registry", ); - assert_close(result.sorptivity, 1.04f64, 0f64, 0f64); } #[test] fn table_2_sandy_loam() { let result = calc_ptf_clapp1978(UsdaTextureClass::SandyLoam); - assert_close(result.b, 4.9f64, 0f64, 0f64); - assert_close(result.saturation_suction, 7.18f64, 0f64, 0f64); - assert_close(result.wetting_front_suction, 9.52f64, 0f64, 0f64); - assert_close(result.saturated_water_content, 0.435f64, 0f64, 0f64); assert_close( - result.saturated_hydraulic_conductivity, - 0.208f64, + result.b, + 4.9f64, + 0.001f64, + 0f64, + "campbell_b", + "dimensionless", + "registry", + ); + assert_close( + result.saturation_suction, + 7.18f64, + 0.01f64, + 0f64, + "matric_potential", + "centimeter_water_head", + "registry", + ); + assert_close( + result.wetting_front_suction, + 9.52f64, + 0.01f64, 0f64, + "matric_potential", + "centimeter_water_head", + "registry", + ); + assert_close( + result.saturated_water_content, + 0.435f64, + 0.001f64, 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", + ); + assert_close( + result.saturated_hydraulic_conductivity, + 0.208f64, + 0.00001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "centimeter_per_minute", + "registry", + ); + assert_close( + result.sorptivity, + 1.03f64, + 0.0001f64, + 0.01f64, + "sorptivity", + "centimeter_per_square_root_minute", + "registry", ); - assert_close(result.sorptivity, 1.03f64, 0f64, 0f64); } #[test] fn table_2_silt_loam() { let result = calc_ptf_clapp1978(UsdaTextureClass::SiltLoam); - assert_close(result.b, 5.3f64, 0f64, 0f64); - assert_close(result.saturation_suction, 56.6f64, 0f64, 0f64); - assert_close(result.wetting_front_suction, 75.3f64, 0f64, 0f64); - assert_close(result.saturated_water_content, 0.485f64, 0f64, 0f64); assert_close( - result.saturated_hydraulic_conductivity, - 0.0432f64, + result.b, + 5.3f64, + 0.001f64, + 0f64, + "campbell_b", + "dimensionless", + "registry", + ); + assert_close( + result.saturation_suction, + 56.6f64, + 0.01f64, 0f64, + "matric_potential", + "centimeter_water_head", + "registry", + ); + assert_close( + result.wetting_front_suction, + 75.3f64, + 0.01f64, + 0f64, + "matric_potential", + "centimeter_water_head", + "registry", + ); + assert_close( + result.saturated_water_content, + 0.485f64, + 0.001f64, 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", + ); + assert_close( + result.saturated_hydraulic_conductivity, + 0.0432f64, + 0.00001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "centimeter_per_minute", + "registry", + ); + assert_close( + result.sorptivity, + 1.26f64, + 0.0001f64, + 0.01f64, + "sorptivity", + "centimeter_per_square_root_minute", + "registry", ); - assert_close(result.sorptivity, 1.26f64, 0f64, 0f64); } #[test] fn table_2_loam() { let result = calc_ptf_clapp1978(UsdaTextureClass::Loam); - assert_close(result.b, 5.39f64, 0f64, 0f64); - assert_close(result.saturation_suction, 14.6f64, 0f64, 0f64); - assert_close(result.wetting_front_suction, 20f64, 0f64, 0f64); - assert_close(result.saturated_water_content, 0.451f64, 0f64, 0f64); assert_close( - result.saturated_hydraulic_conductivity, - 0.0417f64, + result.b, + 5.39f64, + 0.001f64, + 0f64, + "campbell_b", + "dimensionless", + "registry", + ); + assert_close( + result.saturation_suction, + 14.6f64, + 0.01f64, 0f64, + "matric_potential", + "centimeter_water_head", + "registry", + ); + assert_close( + result.wetting_front_suction, + 20f64, + 0.01f64, 0f64, + "matric_potential", + "centimeter_water_head", + "registry", + ); + assert_close( + result.saturated_water_content, + 0.451f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", + ); + assert_close( + result.saturated_hydraulic_conductivity, + 0.0417f64, + 0.00001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "centimeter_per_minute", + "registry", + ); + assert_close( + result.sorptivity, + 0.693f64, + 0.0001f64, + 0.01f64, + "sorptivity", + "centimeter_per_square_root_minute", + "registry", ); - assert_close(result.sorptivity, 0.693f64, 0f64, 0f64); } #[test] fn table_2_sandy_clay_loam() { let result = calc_ptf_clapp1978(UsdaTextureClass::SandyClayLoam); - assert_close(result.b, 7.12f64, 0f64, 0f64); - assert_close(result.saturation_suction, 8.63f64, 0f64, 0f64); - assert_close(result.wetting_front_suction, 11.7f64, 0f64, 0f64); - assert_close(result.saturated_water_content, 0.42f64, 0f64, 0f64); assert_close( - result.saturated_hydraulic_conductivity, - 0.0378f64, + result.b, + 7.12f64, + 0.001f64, 0f64, + "campbell_b", + "dimensionless", + "registry", + ); + assert_close( + result.saturation_suction, + 8.63f64, + 0.01f64, + 0f64, + "matric_potential", + "centimeter_water_head", + "registry", + ); + assert_close( + result.wetting_front_suction, + 11.7f64, + 0.01f64, + 0f64, + "matric_potential", + "centimeter_water_head", + "registry", + ); + assert_close( + result.saturated_water_content, + 0.42f64, + 0.001f64, 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", + ); + assert_close( + result.saturated_hydraulic_conductivity, + 0.0378f64, + 0.00001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "centimeter_per_minute", + "registry", + ); + assert_close( + result.sorptivity, + 0.488f64, + 0.0001f64, + 0.01f64, + "sorptivity", + "centimeter_per_square_root_minute", + "registry", ); - assert_close(result.sorptivity, 0.488f64, 0f64, 0f64); } #[test] fn table_2_silty_clay_loam() { let result = calc_ptf_clapp1978(UsdaTextureClass::SiltyClayLoam); - assert_close(result.b, 7.75f64, 0f64, 0f64); - assert_close(result.saturation_suction, 14.6f64, 0f64, 0f64); - assert_close(result.wetting_front_suction, 19.7f64, 0f64, 0f64); - assert_close(result.saturated_water_content, 0.477f64, 0f64, 0f64); assert_close( - result.saturated_hydraulic_conductivity, - 0.0102f64, + result.b, + 7.75f64, + 0.001f64, + 0f64, + "campbell_b", + "dimensionless", + "registry", + ); + assert_close( + result.saturation_suction, + 14.6f64, + 0.01f64, + 0f64, + "matric_potential", + "centimeter_water_head", + "registry", + ); + assert_close( + result.wetting_front_suction, + 19.7f64, + 0.01f64, 0f64, + "matric_potential", + "centimeter_water_head", + "registry", + ); + assert_close( + result.saturated_water_content, + 0.477f64, + 0.001f64, 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", + ); + assert_close( + result.saturated_hydraulic_conductivity, + 0.0102f64, + 0.00001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "centimeter_per_minute", + "registry", + ); + assert_close( + result.sorptivity, + 0.31f64, + 0.0001f64, + 0.01f64, + "sorptivity", + "centimeter_per_square_root_minute", + "registry", ); - assert_close(result.sorptivity, 0.31f64, 0f64, 0f64); } #[test] fn table_2_clay_loam() { let result = calc_ptf_clapp1978(UsdaTextureClass::ClayLoam); - assert_close(result.b, 8.52f64, 0f64, 0f64); - assert_close(result.saturation_suction, 36.1f64, 0f64, 0f64); - assert_close(result.wetting_front_suction, 48.1f64, 0f64, 0f64); - assert_close(result.saturated_water_content, 0.476f64, 0f64, 0f64); assert_close( - result.saturated_hydraulic_conductivity, - 0.0147f64, + result.b, + 8.52f64, + 0.001f64, 0f64, + "campbell_b", + "dimensionless", + "registry", + ); + assert_close( + result.saturation_suction, + 36.1f64, + 0.01f64, 0f64, + "matric_potential", + "centimeter_water_head", + "registry", + ); + assert_close( + result.wetting_front_suction, + 48.1f64, + 0.01f64, + 0f64, + "matric_potential", + "centimeter_water_head", + "registry", + ); + assert_close( + result.saturated_water_content, + 0.476f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", + ); + assert_close( + result.saturated_hydraulic_conductivity, + 0.0147f64, + 0.00001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "centimeter_per_minute", + "registry", + ); + assert_close( + result.sorptivity, + 0.537f64, + 0.0001f64, + 0.01f64, + "sorptivity", + "centimeter_per_square_root_minute", + "registry", ); - assert_close(result.sorptivity, 0.537f64, 0f64, 0f64); } #[test] fn table_2_sandy_clay() { let result = calc_ptf_clapp1978(UsdaTextureClass::SandyClay); - assert_close(result.b, 10.4f64, 0f64, 0f64); - assert_close(result.saturation_suction, 6.16f64, 0f64, 0f64); - assert_close(result.wetting_front_suction, 8.18f64, 0f64, 0f64); - assert_close(result.saturated_water_content, 0.426f64, 0f64, 0f64); assert_close( - result.saturated_hydraulic_conductivity, - 0.013f64, + result.b, + 10.4f64, + 0.001f64, + 0f64, + "campbell_b", + "dimensionless", + "registry", + ); + assert_close( + result.saturation_suction, + 6.16f64, + 0.01f64, + 0f64, + "matric_potential", + "centimeter_water_head", + "registry", + ); + assert_close( + result.wetting_front_suction, + 8.18f64, + 0.01f64, 0f64, + "matric_potential", + "centimeter_water_head", + "registry", + ); + assert_close( + result.saturated_water_content, + 0.426f64, + 0.001f64, 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", + ); + assert_close( + result.saturated_hydraulic_conductivity, + 0.013f64, + 0.00001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "centimeter_per_minute", + "registry", + ); + assert_close( + result.sorptivity, + 0.223f64, + 0.0001f64, + 0.01f64, + "sorptivity", + "centimeter_per_square_root_minute", + "registry", ); - assert_close(result.sorptivity, 0.223f64, 0f64, 0f64); } #[test] fn table_2_silty_clay() { let result = calc_ptf_clapp1978(UsdaTextureClass::SiltyClay); - assert_close(result.b, 10.4f64, 0f64, 0f64); - assert_close(result.saturation_suction, 17.4f64, 0f64, 0f64); - assert_close(result.wetting_front_suction, 23f64, 0f64, 0f64); - assert_close(result.saturated_water_content, 0.492f64, 0f64, 0f64); assert_close( - result.saturated_hydraulic_conductivity, - 0.0062f64, + result.b, + 10.4f64, + 0.001f64, + 0f64, + "campbell_b", + "dimensionless", + "registry", + ); + assert_close( + result.saturation_suction, + 17.4f64, + 0.01f64, 0f64, + "matric_potential", + "centimeter_water_head", + "registry", + ); + assert_close( + result.wetting_front_suction, + 23f64, + 0.01f64, 0f64, + "matric_potential", + "centimeter_water_head", + "registry", + ); + assert_close( + result.saturated_water_content, + 0.492f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", + ); + assert_close( + result.saturated_hydraulic_conductivity, + 0.0062f64, + 0.00001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "centimeter_per_minute", + "registry", + ); + assert_close( + result.sorptivity, + 0.242f64, + 0.0001f64, + 0.01f64, + "sorptivity", + "centimeter_per_square_root_minute", + "registry", ); - assert_close(result.sorptivity, 0.242f64, 0f64, 0f64); } #[test] fn table_2_clay() { let result = calc_ptf_clapp1978(UsdaTextureClass::Clay); - assert_close(result.b, 11.4f64, 0f64, 0f64); - assert_close(result.saturation_suction, 18.6f64, 0f64, 0f64); - assert_close(result.wetting_front_suction, 24.3f64, 0f64, 0f64); - assert_close(result.saturated_water_content, 0.482f64, 0f64, 0f64); assert_close( - result.saturated_hydraulic_conductivity, - 0.0077f64, + result.b, + 11.4f64, + 0.001f64, 0f64, + "campbell_b", + "dimensionless", + "registry", + ); + assert_close( + result.saturation_suction, + 18.6f64, + 0.01f64, 0f64, + "matric_potential", + "centimeter_water_head", + "registry", + ); + assert_close( + result.wetting_front_suction, + 24.3f64, + 0.01f64, + 0f64, + "matric_potential", + "centimeter_water_head", + "registry", + ); + assert_close( + result.saturated_water_content, + 0.482f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", + ); + assert_close( + result.saturated_hydraulic_conductivity, + 0.0077f64, + 0.00001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "centimeter_per_minute", + "registry", + ); + assert_close( + result.sorptivity, + 0.268f64, + 0.0001f64, + 0.01f64, + "sorptivity", + "centimeter_per_square_root_minute", + "registry", ); - assert_close(result.sorptivity, 0.268f64, 0f64, 0f64); } } diff --git a/targets/ptfkit-rs/src/cosby1984.rs b/targets/ptfkit-rs/src/cosby1984.rs index 2b55c24..4410d6a 100644 --- a/targets/ptfkit-rs/src/cosby1984.rs +++ b/targets/ptfkit-rs/src/cosby1984.rs @@ -16,6 +16,41 @@ United States 1448 soil samples from Holtan et al. (1968) and Rawls et al. (1976)."] +#[cfg(test)] +fn resolved_tolerance(expected: f64, absolute: f64, relative: f64) -> f64 { + absolute + .max(relative * expected.abs()) + .max(0.00000000000001f64) +} +#[cfg(test)] +fn assert_close( + actual: f64, + expected: f64, + absolute: f64, + relative: f64, + quantity: &str, + unit: &str, + source: &str, +) { + let difference = (actual - expected).abs(); + let tolerance = resolved_tolerance(expected, absolute, relative); + assert!( + difference <= tolerance, + "actual={actual}, expected={expected}, difference={difference}, tolerance={tolerance}, quantity={quantity}, unit={unit}, source={source}" + ); +} +#[cfg(test)] +mod comparator_tests { + use super::*; + #[test] + fn accepts_below_and_rejects_above_tolerance() { + for expected in [0.0, 2.0, -2.0] { + let tolerance = resolved_tolerance(expected, 0.001, 0.01); + assert!((expected + tolerance * 0.5 - expected).abs() <= tolerance); + assert!((expected + tolerance * 2.0 - expected).abs() > tolerance); + } + } +} #[doc = r"Results returned by `calc_ptf_cosby1984_univariate`."] #[derive(Clone, Copy, Debug, PartialEq)] pub struct Cosby1984UnivariatePTFResult { @@ -90,46 +125,71 @@ pub fn calc_ptf_cosby1984_univariate( #[cfg(test)] mod tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn scalar_mid_texture() { let result = calc_ptf_cosby1984_univariate(50f64, 30f64, 20f64); - assert_close(result.mean_b, 6.09f64, 0.000000000001f64, 0.000000000001f64); + assert_close( + result.mean_b, + 6.09f64, + 0.001f64, + 0f64, + "campbell_b", + "dimensionless", + "registry", + ); assert_close( result.mean_log_psi_s, 1.225f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "log_saturation_potential", + "reported_log_value", + "registry", ); assert_close( result.mean_log_k_sat, -0.119f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "log_saturated_hydraulic_conductivity", + "reported_log_value", + "registry", ); assert_close( result.mean_theta_s, 42.6f64, - 0.000000000001f64, - 0.000000000001f64, + 0.1f64, + 0f64, + "volumetric_water_content", + "volume_percent", + "registry", + ); + assert_close( + result.sd_b, + 2.34f64, + 0.001f64, + 0f64, + "campbell_b", + "dimensionless", + "registry", ); - assert_close(result.sd_b, 2.34f64, 0.000000000001f64, 0.000000000001f64); assert_close( result.sd_log_k_sat, 0.5553f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "log_saturated_hydraulic_conductivity", + "reported_log_value", + "registry", ); assert_close( result.sd_theta_s, 6.27f64, - 0.000000000001f64, - 0.000000000001f64, + 0.1f64, + 0f64, + "volumetric_water_content", + "volume_percent", + "registry", ); } #[test] @@ -138,39 +198,65 @@ mod tests { assert_close( result.mean_b, 3.705f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "campbell_b", + "dimensionless", + "registry", ); assert_close( result.mean_log_psi_s, 0.832f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "log_saturation_potential", + "reported_log_value", + "registry", ); assert_close( result.mean_log_k_sat, 0.34f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "log_saturated_hydraulic_conductivity", + "reported_log_value", + "registry", ); assert_close( result.mean_theta_s, 38.82f64, - 0.000000000001f64, - 0.000000000001f64, + 0.1f64, + 0f64, + "volumetric_water_content", + "volume_percent", + "registry", + ); + assert_close( + result.sd_b, + 1.59f64, + 0.001f64, + 0f64, + "campbell_b", + "dimensionless", + "registry", ); - assert_close(result.sd_b, 1.59f64, 0.000000000001f64, 0.000000000001f64); assert_close( result.sd_log_k_sat, 0.50715f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "log_saturated_hydraulic_conductivity", + "reported_log_value", + "registry", ); assert_close( result.sd_theta_s, 7.365f64, - 0.000000000001f64, - 0.000000000001f64, + 0.1f64, + 0f64, + "volumetric_water_content", + "volume_percent", + "registry", ); } } diff --git a/targets/ptfkit-rs/src/dharumarajan2019.rs b/targets/ptfkit-rs/src/dharumarajan2019.rs index 9f57a63..1ebc03d 100644 --- a/targets/ptfkit-rs/src/dharumarajan2019.rs +++ b/targets/ptfkit-rs/src/dharumarajan2019.rs @@ -19,6 +19,41 @@ Field capacity and permanent wilting point were modeled from 512 soil samples in Karnataka Plateau and 228 samples in the Southern Karnataka Plateau; infiltration was modeled from 100 Karnataka soil observations."] +#[cfg(test)] +fn resolved_tolerance(expected: f64, absolute: f64, relative: f64) -> f64 { + absolute + .max(relative * expected.abs()) + .max(0.00000000000001f64) +} +#[cfg(test)] +fn assert_close( + actual: f64, + expected: f64, + absolute: f64, + relative: f64, + quantity: &str, + unit: &str, + source: &str, +) { + let difference = (actual - expected).abs(); + let tolerance = resolved_tolerance(expected, absolute, relative); + assert!( + difference <= tolerance, + "actual={actual}, expected={expected}, difference={difference}, tolerance={tolerance}, quantity={quantity}, unit={unit}, source={source}" + ); +} +#[cfg(test)] +mod comparator_tests { + use super::*; + #[test] + fn accepts_below_and_rejects_above_tolerance() { + for expected in [0.0, 2.0, -2.0] { + let tolerance = resolved_tolerance(expected, 0.001, 0.01); + assert!((expected + tolerance * 0.5 - expected).abs() <= tolerance); + assert!((expected + tolerance * 2.0 - expected).abs() > tolerance); + } + } +} #[doc = r"Results returned by `calc_ptf_dharumarajan2019_nkp`."] #[derive(Clone, Copy, Debug, PartialEq)] pub struct Dharumarajan2019WaterRetentionResult { @@ -78,26 +113,26 @@ pub fn calc_ptf_dharumarajan2019_nkp( #[cfg(test)] mod calc_ptf_dharumarajan2019_nkp_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn northern_study_mean_case() { let result = calc_ptf_dharumarajan2019_nkp(43.2f64, 39.8f64, 33.6f64); assert_close( result.field_capacity, 29.7912f64, - 0.000000000001f64, - 0.000000000001f64, + 0.1f64, + 0f64, + "gravimetric_water_content", + "mass_percent", + "registry", ); assert_close( result.permanent_wilting_point, 18.9276f64, - 0.000000000001f64, - 0.000000000001f64, + 0.1f64, + 0f64, + "gravimetric_water_content", + "mass_percent", + "registry", ); } } @@ -144,26 +179,26 @@ pub fn calc_ptf_dharumarajan2019_nkp_clay(clay: f64) -> Dharumarajan2019WaterRet #[cfg(test)] mod calc_ptf_dharumarajan2019_nkp_clay_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn northern_study_mean_clay_case() { let result = calc_ptf_dharumarajan2019_nkp_clay(43.2f64); assert_close( result.field_capacity, 30.2832f64, - 0.000000000001f64, - 0.000000000001f64, + 0.1f64, + 0f64, + "gravimetric_water_content", + "mass_percent", + "registry", ); assert_close( result.permanent_wilting_point, 19.157f64, - 0.000000000001f64, - 0.000000000001f64, + 0.1f64, + 0f64, + "gravimetric_water_content", + "mass_percent", + "registry", ); } } @@ -216,26 +251,26 @@ pub fn calc_ptf_dharumarajan2019_skp( #[cfg(test)] mod calc_ptf_dharumarajan2019_skp_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn southern_study_mean_case() { let result = calc_ptf_dharumarajan2019_skp(31.5f64, 53.5f64, 14.7f64); assert_close( result.field_capacity, 21.8169f64, - 0.000000000001f64, - 0.000000000001f64, + 0.1f64, + 0f64, + "gravimetric_water_content", + "mass_percent", + "registry", ); assert_close( result.permanent_wilting_point, 11.3054f64, - 0.000000000001f64, - 0.000000000001f64, + 0.1f64, + 0f64, + "gravimetric_water_content", + "mass_percent", + "registry", ); } } @@ -280,26 +315,26 @@ pub fn calc_ptf_dharumarajan2019_skp_clay(clay: f64) -> Dharumarajan2019WaterRet #[cfg(test)] mod calc_ptf_dharumarajan2019_skp_clay_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn southern_study_mean_clay_case() { let result = calc_ptf_dharumarajan2019_skp_clay(31.5f64); assert_close( result.field_capacity, 22.0255f64, - 0.000000000001f64, - 0.000000000001f64, + 0.1f64, + 0f64, + "gravimetric_water_content", + "mass_percent", + "registry", ); assert_close( result.permanent_wilting_point, 11.503f64, - 0.000000000001f64, - 0.000000000001f64, + 0.1f64, + 0f64, + "gravimetric_water_content", + "mass_percent", + "registry", ); } } @@ -340,15 +375,17 @@ pub fn calc_ptf_dharumarajan2019_infiltration(sand: f64, silt: f64, clay: f64) - #[cfg(test)] mod calc_ptf_dharumarajan2019_infiltration_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn balanced_texture_case() { let result = calc_ptf_dharumarajan2019_infiltration(50f64, 20f64, 30f64); - assert_close(result, 18.45f64, 0.000000000001f64, 0.000000000001f64); + assert_close( + result, + 18.45f64, + 0.0001f64, + 0.01f64, + "infiltration_rate", + "millimeter_per_hour", + "registry", + ); } } diff --git a/targets/ptfkit-rs/src/ferrerjulia2004.rs b/targets/ptfkit-rs/src/ferrerjulia2004.rs index 17d56bd..42a8664 100644 --- a/targets/ptfkit-rs/src/ferrerjulia2004.rs +++ b/targets/ptfkit-rs/src/ferrerjulia2004.rs @@ -18,6 +18,41 @@ Spanish mainland on the Iberian Peninsula Trueba et al. (2000a) Spanish soil database: 2178 profiles and 7011 horizons; the general regressions used 3172 horizons with sufficient data."] +#[cfg(test)] +fn resolved_tolerance(expected: f64, absolute: f64, relative: f64) -> f64 { + absolute + .max(relative * expected.abs()) + .max(0.00000000000001f64) +} +#[cfg(test)] +fn assert_close( + actual: f64, + expected: f64, + absolute: f64, + relative: f64, + quantity: &str, + unit: &str, + source: &str, +) { + let difference = (actual - expected).abs(); + let tolerance = resolved_tolerance(expected, absolute, relative); + assert!( + difference <= tolerance, + "actual={actual}, expected={expected}, difference={difference}, tolerance={tolerance}, quantity={quantity}, unit={unit}, source={source}" + ); +} +#[cfg(test)] +mod comparator_tests { + use super::*; + #[test] + fn accepts_below_and_rejects_above_tolerance() { + for expected in [0.0, 2.0, -2.0] { + let tolerance = resolved_tolerance(expected, 0.001, 0.01); + assert!((expected + tolerance * 0.5 - expected).abs() <= tolerance); + assert!((expected + tolerance * 2.0 - expected).abs() > tolerance); + } + } +} #[doc = r"Evaluate the Campbell and Shiozawa saturated-conductivity PTF. # Arguments @@ -45,20 +80,17 @@ pub fn calc_ptf_ferrerjulia2004_campbell_shiozawa(sand: f64, clay: f64) -> f64 { #[cfg(test)] mod calc_ptf_ferrerjulia2004_campbell_shiozawa_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn reviewer_reference_vector() { let result = calc_ptf_ferrerjulia2004_campbell_shiozawa(50f64, 25f64); assert_close( result, 0.025071690110308933f64, - 0.000000000001f64, - 0.000000000001f64, + 0.0001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "millimeter_per_hour", + "registry", ); } } @@ -98,20 +130,17 @@ pub fn calc_ptf_ferrerjulia2004_saxton(sand: f64, clay: f64) -> f64 { #[cfg(test)] mod calc_ptf_ferrerjulia2004_saxton_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn reviewer_reference_vector() { let result = calc_ptf_ferrerjulia2004_saxton(50f64, 25f64); assert_close( result, 0.00022673430784327228f64, - 0.000000000001f64, - 0.000000000001f64, + 0.0001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "millimeter_per_hour", + "registry", ); } } @@ -141,20 +170,17 @@ pub fn calc_ptf_ferrerjulia2004_dane_puckett(clay: f64) -> f64 { #[cfg(test)] mod calc_ptf_ferrerjulia2004_dane_puckett_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn reviewer_reference_vector() { let result = calc_ptf_ferrerjulia2004_dane_puckett(25f64); assert_close( result, 8.302039828385373f64, - 0.000000000001f64, - 0.000000000001f64, + 0.0001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "millimeter_per_hour", + "registry", ); } } @@ -184,20 +210,17 @@ pub fn calc_ptf_ferrerjulia2004_puckett(clay: f64) -> f64 { #[cfg(test)] mod calc_ptf_ferrerjulia2004_puckett_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn reviewer_reference_vector() { let result = calc_ptf_ferrerjulia2004_puckett(25f64); assert_close( result, 1.1257967371765654f64, - 0.000000000001f64, - 0.000000000001f64, + 0.0001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "millimeter_per_hour", + "registry", ); } } @@ -228,20 +251,17 @@ pub fn calc_ptf_ferrerjulia2004_cosby(sand: f64, clay: f64) -> f64 { #[cfg(test)] mod calc_ptf_ferrerjulia2004_cosby_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn reviewer_reference_vector() { let result = calc_ptf_ferrerjulia2004_cosby(50f64, 25f64); assert_close( result, 26.091830970918934f64, - 0.000000000001f64, - 0.000000000001f64, + 0.0001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "millimeter_per_hour", + "registry", ); } } @@ -271,20 +291,17 @@ pub fn calc_ptf_ferrerjulia2004_humic_acrisol_sand(sand: f64) -> f64 { #[cfg(test)] mod calc_ptf_ferrerjulia2004_humic_acrisol_sand_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn reviewer_reference_vector() { let result = calc_ptf_ferrerjulia2004_humic_acrisol_sand(50f64); assert_close( result, 14.529879794648146f64, - 0.000000000001f64, - 0.000000000001f64, + 0.0001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "millimeter_per_hour", + "registry", ); } } @@ -320,17 +337,19 @@ pub fn calc_ptf_ferrerjulia2004_humic_acrisol_texture_organic_matter( #[cfg(test)] mod calc_ptf_ferrerjulia2004_humic_acrisol_texture_organic_matter_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn reviewer_reference_vector() { let result = calc_ptf_ferrerjulia2004_humic_acrisol_texture_organic_matter(50f64, 25f64, 2.5f64); - assert_close(result, 19.563475f64, 0.000000000001f64, 0.000000000001f64); + assert_close( + result, + 19.563475f64, + 0.0001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "millimeter_per_hour", + "registry", + ); } } #[doc = r"Estimate saturated conductivity for Calcic Cambisol from sand content. @@ -359,20 +378,17 @@ pub fn calc_ptf_ferrerjulia2004_calcic_cambisol_sand(sand: f64) -> f64 { #[cfg(test)] mod calc_ptf_ferrerjulia2004_calcic_cambisol_sand_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn reviewer_reference_vector() { let result = calc_ptf_ferrerjulia2004_calcic_cambisol_sand(50f64); assert_close( result, 9.995162000935244f64, - 0.000000000001f64, - 0.000000000001f64, + 0.0001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "millimeter_per_hour", + "registry", ); } } @@ -408,17 +424,19 @@ pub fn calc_ptf_ferrerjulia2004_calcic_cambisol_texture_organic_matter( #[cfg(test)] mod calc_ptf_ferrerjulia2004_calcic_cambisol_texture_organic_matter_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn reviewer_reference_vector() { let result = calc_ptf_ferrerjulia2004_calcic_cambisol_texture_organic_matter(50f64, 25f64, 2.5f64); - assert_close(result, 16.51625f64, 0.000000000001f64, 0.000000000001f64); + assert_close( + result, + 16.51625f64, + 0.0001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "millimeter_per_hour", + "registry", + ); } } #[doc = r"Estimate saturated conductivity for Dystric Cambisol from sand content. @@ -447,20 +465,17 @@ pub fn calc_ptf_ferrerjulia2004_dystric_cambisol_sand(sand: f64) -> f64 { #[cfg(test)] mod calc_ptf_ferrerjulia2004_dystric_cambisol_sand_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn reviewer_reference_vector() { let result = calc_ptf_ferrerjulia2004_dystric_cambisol_sand(50f64); assert_close( result, 13.413573870265868f64, - 0.000000000001f64, - 0.000000000001f64, + 0.0001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "millimeter_per_hour", + "registry", ); } } @@ -496,17 +511,19 @@ pub fn calc_ptf_ferrerjulia2004_dystric_cambisol_texture_organic_matter( #[cfg(test)] mod calc_ptf_ferrerjulia2004_dystric_cambisol_texture_organic_matter_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn reviewer_reference_vector() { let result = calc_ptf_ferrerjulia2004_dystric_cambisol_texture_organic_matter(50f64, 25f64, 2.5f64); - assert_close(result, 20.79f64, 0.000000000001f64, 0.000000000001f64); + assert_close( + result, + 20.79f64, + 0.0001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "millimeter_per_hour", + "registry", + ); } } #[doc = r"Estimate saturated conductivity for Eutric Cambisol from sand content. @@ -535,20 +552,17 @@ pub fn calc_ptf_ferrerjulia2004_eutric_cambisol_sand(sand: f64) -> f64 { #[cfg(test)] mod calc_ptf_ferrerjulia2004_eutric_cambisol_sand_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn reviewer_reference_vector() { let result = calc_ptf_ferrerjulia2004_eutric_cambisol_sand(50f64); assert_close( result, 11.570199173737363f64, - 0.000000000001f64, - 0.000000000001f64, + 0.0001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "millimeter_per_hour", + "registry", ); } } @@ -584,17 +598,19 @@ pub fn calc_ptf_ferrerjulia2004_eutric_cambisol_texture_organic_matter( #[cfg(test)] mod calc_ptf_ferrerjulia2004_eutric_cambisol_texture_organic_matter_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn reviewer_reference_vector() { let result = calc_ptf_ferrerjulia2004_eutric_cambisol_texture_organic_matter(50f64, 25f64, 2.5f64); - assert_close(result, 19.5235f64, 0.000000000001f64, 0.000000000001f64); + assert_close( + result, + 19.5235f64, + 0.0001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "millimeter_per_hour", + "registry", + ); } } #[doc = r"Estimate saturated conductivity for Gleyic Cambisol from sand content. @@ -623,20 +639,17 @@ pub fn calc_ptf_ferrerjulia2004_gleyic_cambisol_sand(sand: f64) -> f64 { #[cfg(test)] mod calc_ptf_ferrerjulia2004_gleyic_cambisol_sand_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn reviewer_reference_vector() { let result = calc_ptf_ferrerjulia2004_gleyic_cambisol_sand(50f64); assert_close( result, 6.559110321079926f64, - 0.000000000001f64, - 0.000000000001f64, + 0.0001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "millimeter_per_hour", + "registry", ); } } @@ -672,17 +685,19 @@ pub fn calc_ptf_ferrerjulia2004_gleyic_cambisol_texture_organic_matter( #[cfg(test)] mod calc_ptf_ferrerjulia2004_gleyic_cambisol_texture_organic_matter_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn reviewer_reference_vector() { let result = calc_ptf_ferrerjulia2004_gleyic_cambisol_texture_organic_matter(50f64, 25f64, 2.5f64); - assert_close(result, 11.514f64, 0.000000000001f64, 0.000000000001f64); + assert_close( + result, + 11.514f64, + 0.0001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "millimeter_per_hour", + "registry", + ); } } #[doc = r"Estimate saturated conductivity for Humic Cambisol from sand content. @@ -711,20 +726,17 @@ pub fn calc_ptf_ferrerjulia2004_humic_cambisol_sand(sand: f64) -> f64 { #[cfg(test)] mod calc_ptf_ferrerjulia2004_humic_cambisol_sand_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn reviewer_reference_vector() { let result = calc_ptf_ferrerjulia2004_humic_cambisol_sand(50f64); assert_close( result, 13.820129957492508f64, - 0.000000000001f64, - 0.000000000001f64, + 0.0001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "millimeter_per_hour", + "registry", ); } } @@ -760,17 +772,19 @@ pub fn calc_ptf_ferrerjulia2004_humic_cambisol_texture_organic_matter( #[cfg(test)] mod calc_ptf_ferrerjulia2004_humic_cambisol_texture_organic_matter_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn reviewer_reference_vector() { let result = calc_ptf_ferrerjulia2004_humic_cambisol_texture_organic_matter(50f64, 25f64, 2.5f64); - assert_close(result, 20.9029f64, 0.000000000001f64, 0.000000000001f64); + assert_close( + result, + 20.9029f64, + 0.0001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "millimeter_per_hour", + "registry", + ); } } #[doc = r"Estimate saturated conductivity for Calcaric Fluvisol from sand content. @@ -799,20 +813,17 @@ pub fn calc_ptf_ferrerjulia2004_calcaric_fluvisol_sand(sand: f64) -> f64 { #[cfg(test)] mod calc_ptf_ferrerjulia2004_calcaric_fluvisol_sand_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn reviewer_reference_vector() { let result = calc_ptf_ferrerjulia2004_calcaric_fluvisol_sand(50f64); assert_close( result, 10.959482760491376f64, - 0.000000000001f64, - 0.000000000001f64, + 0.0001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "millimeter_per_hour", + "registry", ); } } @@ -848,17 +859,19 @@ pub fn calc_ptf_ferrerjulia2004_calcaric_fluvisol_texture_organic_matter( #[cfg(test)] mod calc_ptf_ferrerjulia2004_calcaric_fluvisol_texture_organic_matter_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn reviewer_reference_vector() { let result = calc_ptf_ferrerjulia2004_calcaric_fluvisol_texture_organic_matter(50f64, 25f64, 2.5f64); - assert_close(result, 13.101f64, 0.000000000001f64, 0.000000000001f64); + assert_close( + result, + 13.101f64, + 0.0001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "millimeter_per_hour", + "registry", + ); } } #[doc = r"Estimate saturated conductivity for Calcic Luvisol from sand content. @@ -891,20 +904,17 @@ pub fn calc_ptf_ferrerjulia2004_calcic_luvisol_sand(sand: f64) -> f64 { #[cfg(test)] mod calc_ptf_ferrerjulia2004_calcic_luvisol_sand_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn reviewer_reference_vector() { let result = calc_ptf_ferrerjulia2004_calcic_luvisol_sand(50f64); assert_close( result, 6.931067612606957f64, - 0.000000000001f64, - 0.000000000001f64, + 0.0001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "millimeter_per_hour", + "registry", ); } } @@ -944,17 +954,19 @@ pub fn calc_ptf_ferrerjulia2004_calcic_luvisol_texture_organic_matter( #[cfg(test)] mod calc_ptf_ferrerjulia2004_calcic_luvisol_texture_organic_matter_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn reviewer_reference_vector() { let result = calc_ptf_ferrerjulia2004_calcic_luvisol_texture_organic_matter(50f64, 25f64, 2.5f64); - assert_close(result, 8.9332f64, 0.000000000001f64, 0.000000000001f64); + assert_close( + result, + 8.9332f64, + 0.0001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "millimeter_per_hour", + "registry", + ); } } #[doc = r"Estimate saturated conductivity for Chromic Luvisol from sand content. @@ -983,20 +995,17 @@ pub fn calc_ptf_ferrerjulia2004_chromic_luvisol_sand(sand: f64) -> f64 { #[cfg(test)] mod calc_ptf_ferrerjulia2004_chromic_luvisol_sand_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn reviewer_reference_vector() { let result = calc_ptf_ferrerjulia2004_chromic_luvisol_sand(50f64); assert_close( result, 10.61655727277843f64, - 0.000000000001f64, - 0.000000000001f64, + 0.0001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "millimeter_per_hour", + "registry", ); } } @@ -1032,17 +1041,19 @@ pub fn calc_ptf_ferrerjulia2004_chromic_luvisol_texture_organic_matter( #[cfg(test)] mod calc_ptf_ferrerjulia2004_chromic_luvisol_texture_organic_matter_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn reviewer_reference_vector() { let result = calc_ptf_ferrerjulia2004_chromic_luvisol_texture_organic_matter(50f64, 25f64, 2.5f64); - assert_close(result, 17.1465f64, 0.000000000001f64, 0.000000000001f64); + assert_close( + result, + 17.1465f64, + 0.0001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "millimeter_per_hour", + "registry", + ); } } #[doc = r"Estimate saturated conductivity for Gleyic Luvisol from sand content. @@ -1071,20 +1082,17 @@ pub fn calc_ptf_ferrerjulia2004_gleyic_luvisol_sand(sand: f64) -> f64 { #[cfg(test)] mod calc_ptf_ferrerjulia2004_gleyic_luvisol_sand_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn reviewer_reference_vector() { let result = calc_ptf_ferrerjulia2004_gleyic_luvisol_sand(50f64); assert_close( result, 7.049012285884197f64, - 0.000000000001f64, - 0.000000000001f64, + 0.0001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "millimeter_per_hour", + "registry", ); } } @@ -1120,17 +1128,19 @@ pub fn calc_ptf_ferrerjulia2004_gleyic_luvisol_texture_organic_matter( #[cfg(test)] mod calc_ptf_ferrerjulia2004_gleyic_luvisol_texture_organic_matter_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn reviewer_reference_vector() { let result = calc_ptf_ferrerjulia2004_gleyic_luvisol_texture_organic_matter(50f64, 25f64, 2.5f64); - assert_close(result, 19.55f64, 0.000000000001f64, 0.000000000001f64); + assert_close( + result, + 19.55f64, + 0.0001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "millimeter_per_hour", + "registry", + ); } } #[doc = r"Estimate saturated conductivity for Orthic Luvisol from sand content. @@ -1159,20 +1169,17 @@ pub fn calc_ptf_ferrerjulia2004_orthic_luvisol_sand(sand: f64) -> f64 { #[cfg(test)] mod calc_ptf_ferrerjulia2004_orthic_luvisol_sand_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn reviewer_reference_vector() { let result = calc_ptf_ferrerjulia2004_orthic_luvisol_sand(50f64); assert_close( result, 12.04280911180458f64, - 0.000000000001f64, - 0.000000000001f64, + 0.0001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "millimeter_per_hour", + "registry", ); } } @@ -1208,17 +1215,19 @@ pub fn calc_ptf_ferrerjulia2004_orthic_luvisol_texture_organic_matter( #[cfg(test)] mod calc_ptf_ferrerjulia2004_orthic_luvisol_texture_organic_matter_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn reviewer_reference_vector() { let result = calc_ptf_ferrerjulia2004_orthic_luvisol_texture_organic_matter(50f64, 25f64, 2.5f64); - assert_close(result, 21.40425f64, 0.000000000001f64, 0.000000000001f64); + assert_close( + result, + 21.40425f64, + 0.0001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "millimeter_per_hour", + "registry", + ); } } #[doc = r"Estimate saturated conductivity for Ranker from sand content. @@ -1247,20 +1256,17 @@ pub fn calc_ptf_ferrerjulia2004_ranker_sand(sand: f64) -> f64 { #[cfg(test)] mod calc_ptf_ferrerjulia2004_ranker_sand_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn reviewer_reference_vector() { let result = calc_ptf_ferrerjulia2004_ranker_sand(50f64); assert_close( result, 12.423399456061938f64, - 0.000000000001f64, - 0.000000000001f64, + 0.0001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "millimeter_per_hour", + "registry", ); } } @@ -1296,16 +1302,18 @@ pub fn calc_ptf_ferrerjulia2004_ranker_texture_organic_matter( #[cfg(test)] mod calc_ptf_ferrerjulia2004_ranker_texture_organic_matter_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn reviewer_reference_vector() { let result = calc_ptf_ferrerjulia2004_ranker_texture_organic_matter(50f64, 25f64, 2.5f64); - assert_close(result, 22.4515f64, 0.000000000001f64, 0.000000000001f64); + assert_close( + result, + 22.4515f64, + 0.0001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "millimeter_per_hour", + "registry", + ); } } #[doc = r"Estimate saturated conductivity for Calcaric Regosol from sand content. @@ -1334,20 +1342,17 @@ pub fn calc_ptf_ferrerjulia2004_calcaric_regosol_sand(sand: f64) -> f64 { #[cfg(test)] mod calc_ptf_ferrerjulia2004_calcaric_regosol_sand_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn reviewer_reference_vector() { let result = calc_ptf_ferrerjulia2004_calcaric_regosol_sand(50f64); assert_close( result, 12.267374733833096f64, - 0.000000000001f64, - 0.000000000001f64, + 0.0001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "millimeter_per_hour", + "registry", ); } } @@ -1383,17 +1388,19 @@ pub fn calc_ptf_ferrerjulia2004_calcaric_regosol_texture_organic_matter( #[cfg(test)] mod calc_ptf_ferrerjulia2004_calcaric_regosol_texture_organic_matter_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn reviewer_reference_vector() { let result = calc_ptf_ferrerjulia2004_calcaric_regosol_texture_organic_matter(50f64, 25f64, 2.5f64); - assert_close(result, 20.532f64, 0.000000000001f64, 0.000000000001f64); + assert_close( + result, + 20.532f64, + 0.0001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "millimeter_per_hour", + "registry", + ); } } #[doc = r"Estimate saturated conductivity for Dystric Regosol from sand content. @@ -1422,20 +1429,17 @@ pub fn calc_ptf_ferrerjulia2004_dystric_regosol_sand(sand: f64) -> f64 { #[cfg(test)] mod calc_ptf_ferrerjulia2004_dystric_regosol_sand_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn reviewer_reference_vector() { let result = calc_ptf_ferrerjulia2004_dystric_regosol_sand(50f64); assert_close( result, 12.081401313183196f64, - 0.000000000001f64, - 0.000000000001f64, + 0.0001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "millimeter_per_hour", + "registry", ); } } @@ -1471,17 +1475,19 @@ pub fn calc_ptf_ferrerjulia2004_dystric_regosol_texture_organic_matter( #[cfg(test)] mod calc_ptf_ferrerjulia2004_dystric_regosol_texture_organic_matter_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn reviewer_reference_vector() { let result = calc_ptf_ferrerjulia2004_dystric_regosol_texture_organic_matter(50f64, 25f64, 2.5f64); - assert_close(result, 17.1335f64, 0.000000000001f64, 0.000000000001f64); + assert_close( + result, + 17.1335f64, + 0.0001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "millimeter_per_hour", + "registry", + ); } } #[doc = r"Estimate saturated conductivity for Eutric Regosol from sand content. @@ -1510,20 +1516,17 @@ pub fn calc_ptf_ferrerjulia2004_eutric_regosol_sand(sand: f64) -> f64 { #[cfg(test)] mod calc_ptf_ferrerjulia2004_eutric_regosol_sand_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn reviewer_reference_vector() { let result = calc_ptf_ferrerjulia2004_eutric_regosol_sand(50f64); assert_close( result, 13.674049986082728f64, - 0.000000000001f64, - 0.000000000001f64, + 0.0001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "millimeter_per_hour", + "registry", ); } } @@ -1559,17 +1562,19 @@ pub fn calc_ptf_ferrerjulia2004_eutric_regosol_texture_organic_matter( #[cfg(test)] mod calc_ptf_ferrerjulia2004_eutric_regosol_texture_organic_matter_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn reviewer_reference_vector() { let result = calc_ptf_ferrerjulia2004_eutric_regosol_texture_organic_matter(50f64, 25f64, 2.5f64); - assert_close(result, 24.06375f64, 0.000000000001f64, 0.000000000001f64); + assert_close( + result, + 24.06375f64, + 0.0001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "millimeter_per_hour", + "registry", + ); } } #[doc = r"Estimate saturated conductivity for Rendzina from sand content. @@ -1604,20 +1609,17 @@ pub fn calc_ptf_ferrerjulia2004_rendzina_sand(sand: f64) -> f64 { #[cfg(test)] mod calc_ptf_ferrerjulia2004_rendzina_sand_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn reviewer_reference_vector() { let result = calc_ptf_ferrerjulia2004_rendzina_sand(50f64); assert_close( result, 12.515763717130312f64, - 0.000000000001f64, - 0.000000000001f64, + 0.0001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "millimeter_per_hour", + "registry", ); } } @@ -1657,16 +1659,18 @@ pub fn calc_ptf_ferrerjulia2004_rendzina_texture_organic_matter( #[cfg(test)] mod calc_ptf_ferrerjulia2004_rendzina_texture_organic_matter_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn reviewer_reference_vector() { let result = calc_ptf_ferrerjulia2004_rendzina_texture_organic_matter(50f64, 25f64, 2.5f64); - assert_close(result, 16.2905f64, 0.000000000001f64, 0.000000000001f64); + assert_close( + result, + 16.2905f64, + 0.0001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "millimeter_per_hour", + "registry", + ); } } #[doc = r"Estimate saturated conductivity for Gleyic Solonchak from sand content. @@ -1699,20 +1703,17 @@ pub fn calc_ptf_ferrerjulia2004_gleyic_solonchak_sand(sand: f64) -> f64 { #[cfg(test)] mod calc_ptf_ferrerjulia2004_gleyic_solonchak_sand_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn reviewer_reference_vector() { let result = calc_ptf_ferrerjulia2004_gleyic_solonchak_sand(50f64); assert_close( result, 4.883324693640427f64, - 0.000000000001f64, - 0.000000000001f64, + 0.0001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "millimeter_per_hour", + "registry", ); } } @@ -1754,17 +1755,19 @@ pub fn calc_ptf_ferrerjulia2004_gleyic_solonchak_texture_organic_matter( #[cfg(test)] mod calc_ptf_ferrerjulia2004_gleyic_solonchak_texture_organic_matter_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn reviewer_reference_vector() { let result = calc_ptf_ferrerjulia2004_gleyic_solonchak_texture_organic_matter(50f64, 25f64, 2.5f64); - assert_close(result, 10.53583f64, 0.000000000001f64, 0.000000000001f64); + assert_close( + result, + 10.53583f64, + 0.0001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "millimeter_per_hour", + "registry", + ); } } #[doc = r"Estimate saturated conductivity for Spanish soils from sand content. @@ -1798,20 +1801,17 @@ pub fn calc_ptf_ferrerjulia2004_general_sand(sand: f64) -> f64 { #[cfg(test)] mod calc_ptf_ferrerjulia2004_general_sand_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn reviewer_reference_vector() { let result = calc_ptf_ferrerjulia2004_general_sand(50f64); assert_close( result, 10.714718864969111f64, - 0.000000000001f64, - 0.000000000001f64, + 0.0001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "millimeter_per_hour", + "registry", ); } } @@ -1852,15 +1852,17 @@ pub fn calc_ptf_ferrerjulia2004_general_texture_organic_matter( #[cfg(test)] mod calc_ptf_ferrerjulia2004_general_texture_organic_matter_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn reviewer_reference_vector() { let result = calc_ptf_ferrerjulia2004_general_texture_organic_matter(50f64, 25f64, 2.5f64); - assert_close(result, 20.06325f64, 0.000000000001f64, 0.000000000001f64); + assert_close( + result, + 20.06325f64, + 0.0001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "millimeter_per_hour", + "registry", + ); } } diff --git a/targets/ptfkit-rs/src/hodnett2002.rs b/targets/ptfkit-rs/src/hodnett2002.rs index 418f71f..8ec8b51 100644 --- a/targets/ptfkit-rs/src/hodnett2002.rs +++ b/targets/ptfkit-rs/src/hodnett2002.rs @@ -18,6 +18,41 @@ Tropical soils between approximately 25 degrees N and 25 degrees S. The IGBP-DIS tropical-soil dataset contains 771 retained horizons from 249 profiles in 22 countries, split into 492 calibration curves and 279 validation curves."] +#[cfg(test)] +fn resolved_tolerance(expected: f64, absolute: f64, relative: f64) -> f64 { + absolute + .max(relative * expected.abs()) + .max(0.00000000000001f64) +} +#[cfg(test)] +fn assert_close( + actual: f64, + expected: f64, + absolute: f64, + relative: f64, + quantity: &str, + unit: &str, + source: &str, +) { + let difference = (actual - expected).abs(); + let tolerance = resolved_tolerance(expected, absolute, relative); + assert!( + difference <= tolerance, + "actual={actual}, expected={expected}, difference={difference}, tolerance={tolerance}, quantity={quantity}, unit={unit}, source={source}" + ); +} +#[cfg(test)] +mod comparator_tests { + use super::*; + #[test] + fn accepts_below_and_rejects_above_tolerance() { + for expected in [0.0, 2.0, -2.0] { + let tolerance = resolved_tolerance(expected, 0.001, 0.01); + assert!((expected + tolerance * 0.5 - expected).abs() <= tolerance); + assert!((expected + tolerance * 2.0 - expected).abs() > tolerance); + } + } +} #[doc = r"Results returned by `calc_ptf_hodnett2002`."] #[derive(Clone, Copy, Debug, PartialEq)] pub struct Hodnett2002PTFResult { @@ -118,12 +153,6 @@ pub fn calc_ptf_hodnett2002( #[cfg(test)] mod tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn calibration_dataset_mean_properties() { let result = @@ -131,26 +160,38 @@ mod tests { assert_close( result.alpha, 0.245183615037873f64, - 0.000000000001f64, - 0.000000000001f64, + 0.00001f64, + 0f64, + "van_genuchten_alpha", + "inverse_kilopascal", + "registry", ); assert_close( result.n, 1.36739326352383f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "van_genuchten_n", + "dimensionless", + "registry", ); assert_close( result.theta_s, 0.4993353f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", ); assert_close( result.theta_r, 0.21344216f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", ); } } diff --git a/targets/ptfkit-rs/src/jabro1992.rs b/targets/ptfkit-rs/src/jabro1992.rs index 342af22..bb27b2f 100644 --- a/targets/ptfkit-rs/src/jabro1992.rs +++ b/targets/ptfkit-rs/src/jabro1992.rs @@ -17,6 +17,41 @@ USA Southern Cooperation Series Bulletins (Dan et al., 1983; Nofziger et al., 1983; Quisenberry et al., 1987), 350 samples; validation on Duffield silt loam data."] +#[cfg(test)] +fn resolved_tolerance(expected: f64, absolute: f64, relative: f64) -> f64 { + absolute + .max(relative * expected.abs()) + .max(0.00000000000001f64) +} +#[cfg(test)] +fn assert_close( + actual: f64, + expected: f64, + absolute: f64, + relative: f64, + quantity: &str, + unit: &str, + source: &str, +) { + let difference = (actual - expected).abs(); + let tolerance = resolved_tolerance(expected, absolute, relative); + assert!( + difference <= tolerance, + "actual={actual}, expected={expected}, difference={difference}, tolerance={tolerance}, quantity={quantity}, unit={unit}, source={source}" + ); +} +#[cfg(test)] +mod comparator_tests { + use super::*; + #[test] + fn accepts_below_and_rejects_above_tolerance() { + for expected in [0.0, 2.0, -2.0] { + let tolerance = resolved_tolerance(expected, 0.001, 0.01); + assert!((expected + tolerance * 0.5 - expected).abs() <= tolerance); + assert!((expected + tolerance * 2.0 - expected).abs() > tolerance); + } + } +} #[doc = r"Estimate saturated hydraulic conductivity from silt, clay, and bulk density. # Arguments @@ -51,20 +86,17 @@ pub fn calc_ptf_jabro1992(silt: f64, clay: f64, bulk_density: f64) -> f64 { #[cfg(test)] mod tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn loamy_sand_min_bd() { let result = calc_ptf_jabro1992(10f64, 5f64, 1.26f64); assert_close( result, 0.0003849640675896946f64, - 0.000000000001f64, - 0.00000001f64, + 0.0000000001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "meter_per_second", + "registry", ); } #[test] @@ -73,8 +105,11 @@ mod tests { assert_close( result, 0.000009804037952717678f64, - 0.000000000001f64, - 0.00000001f64, + 0.0000000001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "meter_per_second", + "registry", ); } #[test] @@ -83,8 +118,11 @@ mod tests { assert_close( result, 0.000000007292435947882127f64, - 0.000000000001f64, - 0.00000001f64, + 0.0000000001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "meter_per_second", + "registry", ); } #[test] @@ -93,8 +131,11 @@ mod tests { assert_close( result, 0.00002032824027706267f64, - 0.000000000001f64, - 0.00000001f64, + 0.0000000001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "meter_per_second", + "registry", ); } } diff --git a/targets/ptfkit-rs/src/li2007.rs b/targets/ptfkit-rs/src/li2007.rs index d70576d..a8b8f24 100644 --- a/targets/ptfkit-rs/src/li2007.rs +++ b/targets/ptfkit-rs/src/li2007.rs @@ -18,6 +18,41 @@ Fengqiu County soils in the North China Plain, China 63 soil water retention curves and 36 saturated soil hydraulic conductivity samples from seven soil profiles."] +#[cfg(test)] +fn resolved_tolerance(expected: f64, absolute: f64, relative: f64) -> f64 { + absolute + .max(relative * expected.abs()) + .max(0.00000000000001f64) +} +#[cfg(test)] +fn assert_close( + actual: f64, + expected: f64, + absolute: f64, + relative: f64, + quantity: &str, + unit: &str, + source: &str, +) { + let difference = (actual - expected).abs(); + let tolerance = resolved_tolerance(expected, absolute, relative); + assert!( + difference <= tolerance, + "actual={actual}, expected={expected}, difference={difference}, tolerance={tolerance}, quantity={quantity}, unit={unit}, source={source}" + ); +} +#[cfg(test)] +mod comparator_tests { + use super::*; + #[test] + fn accepts_below_and_rejects_above_tolerance() { + for expected in [0.0, 2.0, -2.0] { + let tolerance = resolved_tolerance(expected, 0.001, 0.01); + assert!((expected + tolerance * 0.5 - expected).abs() <= tolerance); + assert!((expected + tolerance * 2.0 - expected).abs() > tolerance); + } + } +} #[doc = r"Results returned by `calc_ptf_li2007`."] #[derive(Clone, Copy, Debug, PartialEq)] pub struct Li2007PTFResult { @@ -102,38 +137,44 @@ pub fn calc_ptf_li2007( #[cfg(test)] mod tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn loamy_sand() { let result = calc_ptf_li2007(85f64, 10f64, 5f64, 1.2f64, 0.21f64); assert_close( result.theta_s, 0.5256803583157499f64, - 0.000000000001f64, - 0.00000001f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", ); assert_close( result.a_vg, 0.9491464758307142f64, - 0.000000000001f64, - 0.00000001f64, + 0.00001f64, + 0f64, + "van_genuchten_alpha", + "inverse_centimeter", + "registry", ); assert_close( result.n_vg, 1.1657804980997006f64, - 0.000000000001f64, - 0.00000001f64, + 0.001f64, + 0f64, + "van_genuchten_n", + "dimensionless", + "registry", ); assert_close( result.k_sat, 0.000006549110367333547f64, - 0.000000000001f64, - 0.00000001f64, + 0.0000000001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "meter_per_second", + "registry", ); } #[test] @@ -142,26 +183,38 @@ mod tests { assert_close( result.theta_s, 0.49659526127697506f64, - 0.000000000001f64, - 0.00000001f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", ); assert_close( result.a_vg, 0.009519989841950734f64, - 0.000000000001f64, - 0.00000001f64, + 0.00001f64, + 0f64, + "van_genuchten_alpha", + "inverse_centimeter", + "registry", ); assert_close( result.n_vg, 1.1806286355149054f64, - 0.000000000001f64, - 0.00000001f64, + 0.001f64, + 0f64, + "van_genuchten_n", + "dimensionless", + "registry", ); assert_close( result.k_sat, 0.00000045117324656202257f64, - 0.000000000001f64, - 0.00000001f64, + 0.0000000001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "meter_per_second", + "registry", ); } #[test] @@ -170,26 +223,38 @@ mod tests { assert_close( result.theta_s, 0.4053061510618609f64, - 0.000000000001f64, - 0.00000001f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", ); assert_close( result.a_vg, 0.0018530400762371828f64, - 0.000000000001f64, - 0.00000001f64, + 0.00001f64, + 0f64, + "van_genuchten_alpha", + "inverse_centimeter", + "registry", ); assert_close( result.n_vg, 1.2080428739797433f64, - 0.000000000001f64, - 0.00000001f64, + 0.001f64, + 0f64, + "van_genuchten_n", + "dimensionless", + "registry", ); assert_close( result.k_sat, 0.0000015151432632107234f64, - 0.000000000001f64, - 0.00000001f64, + 0.0000000001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "meter_per_second", + "registry", ); } } diff --git a/targets/ptfkit-rs/src/mayr1999.rs b/targets/ptfkit-rs/src/mayr1999.rs index 5a324d2..f205057 100644 --- a/targets/ptfkit-rs/src/mayr1999.rs +++ b/targets/ptfkit-rs/src/mayr1999.rs @@ -18,6 +18,41 @@ Soil Survey and Land Research Centre soil physical properties database; regressi soil horizons retained from a 306-horizon subset after excluding fits with RMSE greater than 0.05 m^3/m^3, and validation used 1678 independent soil horizons."] +#[cfg(test)] +fn resolved_tolerance(expected: f64, absolute: f64, relative: f64) -> f64 { + absolute + .max(relative * expected.abs()) + .max(0.00000000000001f64) +} +#[cfg(test)] +fn assert_close( + actual: f64, + expected: f64, + absolute: f64, + relative: f64, + quantity: &str, + unit: &str, + source: &str, +) { + let difference = (actual - expected).abs(); + let tolerance = resolved_tolerance(expected, absolute, relative); + assert!( + difference <= tolerance, + "actual={actual}, expected={expected}, difference={difference}, tolerance={tolerance}, quantity={quantity}, unit={unit}, source={source}" + ); +} +#[cfg(test)] +mod comparator_tests { + use super::*; + #[test] + fn accepts_below_and_rejects_above_tolerance() { + for expected in [0.0, 2.0, -2.0] { + let tolerance = resolved_tolerance(expected, 0.001, 0.01); + assert!((expected + tolerance * 0.5 - expected).abs() <= tolerance); + assert!((expected + tolerance * 2.0 - expected).abs() > tolerance); + } + } +} #[doc = r"Results returned by `calc_ptf_mayr1999`."] #[derive(Clone, Copy, Debug, PartialEq)] pub struct Mayr1999PTFResult { @@ -106,32 +141,35 @@ pub fn calc_ptf_mayr1999( #[cfg(test)] mod tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn representative_loam() { let result = calc_ptf_mayr1999(40f64, 40f64, 20f64, 1.3f64, 2f64); assert_close( result.a_hc, 9.19922782578422f64, - 0.000000000001f64, - 0.000000000001f64, + 0.01f64, + 0f64, + "matric_potential", + "centimeter_water_head", + "registry", ); assert_close( result.b_hc, 8.52432443785475f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "hutson_cass_b", + "dimensionless", + "registry", ); assert_close( result.theta_s, 0.48733161333f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", ); } #[test] @@ -140,20 +178,29 @@ mod tests { assert_close( result.a_hc, 14.5305137640911f64, - 0.000000000001f64, - 0.000000000001f64, + 0.01f64, + 0f64, + "matric_potential", + "centimeter_water_head", + "registry", ); assert_close( result.b_hc, 18.9375141190925f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "hutson_cass_b", + "dimensionless", + "registry", ); assert_close( result.theta_s, 0.52145684162f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", ); } #[test] @@ -162,20 +209,29 @@ mod tests { assert_close( result.a_hc, 2.40216699550362f64, - 0.000000000001f64, - 0.000000000001f64, + 0.01f64, + 0f64, + "matric_potential", + "centimeter_water_head", + "registry", ); assert_close( result.b_hc, 3.2690464531591f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "hutson_cass_b", + "dimensionless", + "registry", ); assert_close( result.theta_s, 0.4209756915f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", ); } } diff --git a/targets/ptfkit-rs/src/oosterveld1980.rs b/targets/ptfkit-rs/src/oosterveld1980.rs index fc61368..f259345 100644 --- a/targets/ptfkit-rs/src/oosterveld1980.rs +++ b/targets/ptfkit-rs/src/oosterveld1980.rs @@ -16,6 +16,41 @@ Southern Alberta, Canada 1,137 pressure-plate analyses on 298 soil samples from the Lethbridge Research Station laboratory; the field-capacity tension regression used 134 samples."] +#[cfg(test)] +fn resolved_tolerance(expected: f64, absolute: f64, relative: f64) -> f64 { + absolute + .max(relative * expected.abs()) + .max(0.00000000000001f64) +} +#[cfg(test)] +fn assert_close( + actual: f64, + expected: f64, + absolute: f64, + relative: f64, + quantity: &str, + unit: &str, + source: &str, +) { + let difference = (actual - expected).abs(); + let tolerance = resolved_tolerance(expected, absolute, relative); + assert!( + difference <= tolerance, + "actual={actual}, expected={expected}, difference={difference}, tolerance={tolerance}, quantity={quantity}, unit={unit}, source={source}" + ); +} +#[cfg(test)] +mod comparator_tests { + use super::*; + #[test] + fn accepts_below_and_rejects_above_tolerance() { + for expected in [0.0, 2.0, -2.0] { + let tolerance = resolved_tolerance(expected, 0.001, 0.01); + assert!((expected + tolerance * 0.5 - expected).abs() <= tolerance); + assert!((expected + tolerance * 2.0 - expected).abs() > tolerance); + } + } +} #[doc = r"Estimate field-capacity tension from clay content. # Arguments @@ -45,20 +80,17 @@ pub fn calc_ptf_oosterveld1980_field_capacity_tension(clay: f64) -> f64 { #[cfg(test)] mod calc_ptf_oosterveld1980_field_capacity_tension_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn table_1_loamy_sand() { let result = calc_ptf_oosterveld1980_field_capacity_tension(6.6f64); assert_close( result, 11.8540994164288f64, - 0.000000000001f64, - 0.000000000001f64, + 0.01f64, + 0f64, + "matric_potential", + "kilopascal", + "registry", ); } } @@ -101,20 +133,17 @@ pub fn calc_ptf_oosterveld1980_retention( #[cfg(test)] mod calc_ptf_oosterveld1980_retention_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn table_1_loamy_sand_inputs() { let result = calc_ptf_oosterveld1980_retention(6.6f64, 86.3f64, 105f64, 11.8f64); assert_close( result, 8.2782468100413f64, - 0.000000000001f64, - 0.000000000001f64, + 0.1f64, + 0f64, + "gravimetric_water_content", + "mass_percent", + "registry", ); } } @@ -148,20 +177,17 @@ pub fn calc_ptf_oosterveld1980_field_capacity(clay: f64, sand: f64, mean_depth: #[cfg(test)] mod calc_ptf_oosterveld1980_field_capacity_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn table_1_loamy_sand() { let result = calc_ptf_oosterveld1980_field_capacity(6.6f64, 86.3f64, 105f64); assert_close( result, 8.14707947394088f64, - 0.000000000001f64, - 0.000000000001f64, + 0.1f64, + 0f64, + "gravimetric_water_content", + "mass_percent", + "registry", ); } } @@ -195,16 +221,18 @@ pub fn calc_ptf_oosterveld1980_wilting_point(clay: f64, sand: f64, mean_depth: f #[cfg(test)] mod calc_ptf_oosterveld1980_wilting_point_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn table_1_loamy_sand() { let result = calc_ptf_oosterveld1980_wilting_point(6.6f64, 86.3f64, 105f64); - assert_close(result, 1.3942f64, 0.000000000001f64, 0.000000000001f64); + assert_close( + result, + 1.3942f64, + 0.1f64, + 0f64, + "gravimetric_water_content", + "mass_percent", + "registry", + ); } } #[doc = r"Estimate available gravimetric soil moisture between field capacity and wilting point. @@ -243,20 +271,17 @@ pub fn calc_ptf_oosterveld1980_available_water(clay: f64, sand: f64, mean_depth: #[cfg(test)] mod calc_ptf_oosterveld1980_available_water_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn table_1_loamy_sand() { let result = calc_ptf_oosterveld1980_available_water(6.6f64, 86.3f64, 105f64); assert_close( result, 6.75287947394088f64, - 0.000000000001f64, - 0.000000000001f64, + 0.1f64, + 0f64, + "gravimetric_water_content", + "mass_percent", + "registry", ); } } diff --git a/targets/ptfkit-rs/src/pidgeon1972.rs b/targets/ptfkit-rs/src/pidgeon1972.rs index f480124..4079256 100644 --- a/targets/ptfkit-rs/src/pidgeon1972.rs +++ b/targets/ptfkit-rs/src/pidgeon1972.rs @@ -16,6 +16,41 @@ Non-alluvial ferrallitic soils in Uganda, predominantly kaolinitic and possibly Ugandan profile samples; Entebbe alluvial and Nabbongo montmorillonitic soils were excluded from the adopted regressions."] +#[cfg(test)] +fn resolved_tolerance(expected: f64, absolute: f64, relative: f64) -> f64 { + absolute + .max(relative * expected.abs()) + .max(0.00000000000001f64) +} +#[cfg(test)] +fn assert_close( + actual: f64, + expected: f64, + absolute: f64, + relative: f64, + quantity: &str, + unit: &str, + source: &str, +) { + let difference = (actual - expected).abs(); + let tolerance = resolved_tolerance(expected, absolute, relative); + assert!( + difference <= tolerance, + "actual={actual}, expected={expected}, difference={difference}, tolerance={tolerance}, quantity={quantity}, unit={unit}, source={source}" + ); +} +#[cfg(test)] +mod comparator_tests { + use super::*; + #[test] + fn accepts_below_and_rejects_above_tolerance() { + for expected in [0.0, 2.0, -2.0] { + let tolerance = resolved_tolerance(expected, 0.001, 0.01); + assert!((expected + tolerance * 0.5 - expected).abs() <= tolerance); + assert!((expected + tolerance * 2.0 - expected).abs() > tolerance); + } + } +} #[doc = r"Estimate gravimetric field capacity from silt, clay, and organic matter. # Arguments @@ -44,16 +79,18 @@ pub fn calc_ptf_pidgeon1972_fc(silt: f64, clay: f64, organic_matter: f64) -> f64 #[cfg(test)] mod calc_ptf_pidgeon1972_fc_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn regression_case() { let result = calc_ptf_pidgeon1972_fc(30f64, 20f64, 2f64); - assert_close(result, 21.26f64, 0.000000000001f64, 0.000000000001f64); + assert_close( + result, + 21.26f64, + 0.1f64, + 0f64, + "gravimetric_water_content", + "mass_percent", + "registry", + ); } } #[doc = r"Estimate gravimetric field capacity from sand. @@ -81,16 +118,18 @@ pub fn calc_ptf_pidgeon1972_fc_sand(sand: f64) -> f64 { #[cfg(test)] mod calc_ptf_pidgeon1972_fc_sand_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn regression_case() { let result = calc_ptf_pidgeon1972_fc_sand(50f64); - assert_close(result, 23.66f64, 0.000000000001f64, 0.000000000001f64); + assert_close( + result, + 23.66f64, + 0.1f64, + 0f64, + "gravimetric_water_content", + "mass_percent", + "registry", + ); } } #[doc = r"Estimate gravimetric field capacity from sand and organic matter. @@ -119,16 +158,18 @@ pub fn calc_ptf_pidgeon1972_fc_sand_organic_matter(sand: f64, organic_matter: f6 #[cfg(test)] mod calc_ptf_pidgeon1972_fc_sand_organic_matter_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn regression_case() { let result = calc_ptf_pidgeon1972_fc_sand_organic_matter(50f64, 2f64); - assert_close(result, 23.27f64, 0.000000000001f64, 0.000000000001f64); + assert_close( + result, + 23.27f64, + 0.1f64, + 0f64, + "gravimetric_water_content", + "mass_percent", + "registry", + ); } } #[doc = r"Estimate volumetric field capacity from sand and organic matter. @@ -157,16 +198,18 @@ pub fn calc_ptf_pidgeon1972_fc_vol_sand_organic_matter(sand: f64, organic_matter #[cfg(test)] mod calc_ptf_pidgeon1972_fc_vol_sand_organic_matter_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn regression_case() { let result = calc_ptf_pidgeon1972_fc_vol_sand_organic_matter(50f64, 2f64); - assert_close(result, 31.19f64, 0.000000000001f64, 0.000000000001f64); + assert_close( + result, + 31.19f64, + 0.1f64, + 0f64, + "volumetric_water_content", + "volume_percent", + "registry", + ); } } #[doc = r"Estimate permanent wilting point from silt, clay, and organic matter. @@ -196,16 +239,18 @@ pub fn calc_ptf_pidgeon1972_pwp(silt: f64, clay: f64, organic_matter: f64) -> f6 #[cfg(test)] mod calc_ptf_pidgeon1972_pwp_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn regression_case() { let result = calc_ptf_pidgeon1972_pwp(30f64, 20f64, 2f64); - assert_close(result, 11.11f64, 0.000000000001f64, 0.000000000001f64); + assert_close( + result, + 11.11f64, + 0.1f64, + 0f64, + "gravimetric_water_content", + "mass_percent", + "registry", + ); } } #[doc = r"Estimate permanent wilting point from sand. @@ -233,16 +278,18 @@ pub fn calc_ptf_pidgeon1972_pwp_sand(sand: f64) -> f64 { #[cfg(test)] mod calc_ptf_pidgeon1972_pwp_sand_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn regression_case() { let result = calc_ptf_pidgeon1972_pwp_sand(50f64); - assert_close(result, 13.91f64, 0.000000000001f64, 0.000000000001f64); + assert_close( + result, + 13.91f64, + 0.1f64, + 0f64, + "gravimetric_water_content", + "mass_percent", + "registry", + ); } } #[doc = r"Estimate permanent wilting point from sand and organic matter. @@ -271,16 +318,18 @@ pub fn calc_ptf_pidgeon1972_pwp_sand_organic_matter(sand: f64, organic_matter: f #[cfg(test)] mod calc_ptf_pidgeon1972_pwp_sand_organic_matter_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn regression_case() { let result = calc_ptf_pidgeon1972_pwp_sand_organic_matter(50f64, 2f64); - assert_close(result, 15.28f64, 0.000000000001f64, 0.000000000001f64); + assert_close( + result, + 15.28f64, + 0.1f64, + 0f64, + "gravimetric_water_content", + "mass_percent", + "registry", + ); } } #[doc = r"Estimate available water capacity from clay and organic matter. @@ -309,16 +358,18 @@ pub fn calc_ptf_pidgeon1972_awc(clay: f64, organic_matter: f64) -> f64 { #[cfg(test)] mod calc_ptf_pidgeon1972_awc_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn regression_case() { let result = calc_ptf_pidgeon1972_awc(20f64, 2f64); - assert_close(result, 151.48f64, 0.000000000001f64, 0.000000000001f64); + assert_close( + result, + 151.48f64, + 0.1f64, + 0f64, + "available_water_capacity", + "millimeter_per_meter", + "registry", + ); } } #[doc = r"Estimate available water capacity from sand and organic matter. @@ -347,16 +398,18 @@ pub fn calc_ptf_pidgeon1972_awc_sand_organic_matter(sand: f64, organic_matter: f #[cfg(test)] mod calc_ptf_pidgeon1972_awc_sand_organic_matter_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn regression_case() { let result = calc_ptf_pidgeon1972_awc_sand_organic_matter(50f64, 2f64); - assert_close(result, 109.24f64, 0.000000000001f64, 0.000000000001f64); + assert_close( + result, + 109.24f64, + 0.1f64, + 0f64, + "available_water_capacity", + "millimeter_per_meter", + "registry", + ); } } #[doc = r"Estimate available water capacity from coarse sand. @@ -384,16 +437,18 @@ pub fn calc_ptf_pidgeon1972_awc_coarse_sand(coarse_sand: f64) -> f64 { #[cfg(test)] mod calc_ptf_pidgeon1972_awc_coarse_sand_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn regression_case() { let result = calc_ptf_pidgeon1972_awc_coarse_sand(20f64); - assert_close(result, 115.1f64, 0.000000000001f64, 0.000000000001f64); + assert_close( + result, + 115.1f64, + 0.1f64, + 0f64, + "available_water_capacity", + "millimeter_per_meter", + "registry", + ); } } #[doc = r"Estimate available water capacity from fine sand. @@ -421,16 +476,18 @@ pub fn calc_ptf_pidgeon1972_awc_fine_sand(fine_sand: f64) -> f64 { #[cfg(test)] mod calc_ptf_pidgeon1972_awc_fine_sand_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn regression_case() { let result = calc_ptf_pidgeon1972_awc_fine_sand(20f64); - assert_close(result, 119.9f64, 0.000000000001f64, 0.000000000001f64); + assert_close( + result, + 119.9f64, + 0.1f64, + 0f64, + "available_water_capacity", + "millimeter_per_meter", + "registry", + ); } } #[doc = r"Estimate available water capacity from very fine sand. @@ -458,16 +515,18 @@ pub fn calc_ptf_pidgeon1972_awc_very_fine_sand(very_fine_sand: f64) -> f64 { #[cfg(test)] mod calc_ptf_pidgeon1972_awc_very_fine_sand_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn regression_case() { let result = calc_ptf_pidgeon1972_awc_very_fine_sand(10f64); - assert_close(result, 112.7f64, 0.000000000001f64, 0.000000000001f64); + assert_close( + result, + 112.7f64, + 0.1f64, + 0f64, + "available_water_capacity", + "millimeter_per_meter", + "registry", + ); } } #[doc = r"Estimate extended available water capacity from silt, clay, and organic matter. @@ -497,16 +556,18 @@ pub fn calc_ptf_pidgeon1972_eawc(silt: f64, clay: f64, organic_matter: f64) -> f #[cfg(test)] mod calc_ptf_pidgeon1972_eawc_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn regression_case() { let result = calc_ptf_pidgeon1972_eawc(30f64, 20f64, 2f64); - assert_close(result, 16.12f64, 0.000000000001f64, 0.000000000001f64); + assert_close( + result, + 16.12f64, + 0.1f64, + 0f64, + "available_water_capacity", + "millimeter_per_meter", + "registry", + ); } } #[doc = r"Estimate extended available water capacity from sand. @@ -534,16 +595,18 @@ pub fn calc_ptf_pidgeon1972_eawc_sand(sand: f64) -> f64 { #[cfg(test)] mod calc_ptf_pidgeon1972_eawc_sand_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn regression_case() { let result = calc_ptf_pidgeon1972_eawc_sand(50f64); - assert_close(result, 51.7f64, 0.000000000001f64, 0.000000000001f64); + assert_close( + result, + 51.7f64, + 0.1f64, + 0f64, + "available_water_capacity", + "millimeter_per_meter", + "registry", + ); } } #[doc = r"Estimate extended available water capacity from sand and organic matter. @@ -572,16 +635,18 @@ pub fn calc_ptf_pidgeon1972_eawc_sand_organic_matter(sand: f64, organic_matter: #[cfg(test)] mod calc_ptf_pidgeon1972_eawc_sand_organic_matter_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn regression_case() { let result = calc_ptf_pidgeon1972_eawc_sand_organic_matter(50f64, 2f64); - assert_close(result, 56.26f64, 0.000000000001f64, 0.000000000001f64); + assert_close( + result, + 56.26f64, + 0.1f64, + 0f64, + "available_water_capacity", + "millimeter_per_meter", + "registry", + ); } } #[doc = r"Estimate extended available water capacity from coarse sand and organic matter. @@ -613,16 +678,18 @@ pub fn calc_ptf_pidgeon1972_eawc_coarse_sand_organic_matter( #[cfg(test)] mod calc_ptf_pidgeon1972_eawc_coarse_sand_organic_matter_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn regression_case() { let result = calc_ptf_pidgeon1972_eawc_coarse_sand_organic_matter(20f64, 2f64); - assert_close(result, 53.72f64, 0.000000000001f64, 0.000000000001f64); + assert_close( + result, + 53.72f64, + 0.1f64, + 0f64, + "available_water_capacity", + "millimeter_per_meter", + "registry", + ); } } #[doc = r"Estimate extended available water capacity from fine sand and organic matter. @@ -654,15 +721,17 @@ pub fn calc_ptf_pidgeon1972_eawc_fine_sand_organic_matter( #[cfg(test)] mod calc_ptf_pidgeon1972_eawc_fine_sand_organic_matter_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn regression_case() { let result = calc_ptf_pidgeon1972_eawc_fine_sand_organic_matter(20f64, 2f64); - assert_close(result, 59.58f64, 0.000000000001f64, 0.000000000001f64); + assert_close( + result, + 59.58f64, + 0.1f64, + 0f64, + "available_water_capacity", + "millimeter_per_meter", + "registry", + ); } } diff --git a/targets/ptfkit-rs/src/puckett1985.rs b/targets/ptfkit-rs/src/puckett1985.rs index 2d26d7d..301203a 100644 --- a/targets/ptfkit-rs/src/puckett1985.rs +++ b/targets/ptfkit-rs/src/puckett1985.rs @@ -16,6 +16,41 @@ Lower Coastal Plain of Alabama, USA Seven pedons at seven locations representing six Ultisol series with similar genesis and clay mineralogy."] +#[cfg(test)] +fn resolved_tolerance(expected: f64, absolute: f64, relative: f64) -> f64 { + absolute + .max(relative * expected.abs()) + .max(0.00000000000001f64) +} +#[cfg(test)] +fn assert_close( + actual: f64, + expected: f64, + absolute: f64, + relative: f64, + quantity: &str, + unit: &str, + source: &str, +) { + let difference = (actual - expected).abs(); + let tolerance = resolved_tolerance(expected, absolute, relative); + assert!( + difference <= tolerance, + "actual={actual}, expected={expected}, difference={difference}, tolerance={tolerance}, quantity={quantity}, unit={unit}, source={source}" + ); +} +#[cfg(test)] +mod comparator_tests { + use super::*; + #[test] + fn accepts_below_and_rejects_above_tolerance() { + for expected in [0.0, 2.0, -2.0] { + let tolerance = resolved_tolerance(expected, 0.001, 0.01); + assert!((expected + tolerance * 0.5 - expected).abs() <= tolerance); + assert!((expected + tolerance * 2.0 - expected).abs() > tolerance); + } + } +} #[doc = r"Results returned by `calc_ptf_puckett1985`."] #[derive(Clone, Copy, Debug, PartialEq)] pub struct Puckett1985PTFResult { @@ -107,70 +142,107 @@ pub fn calc_ptf_puckett1985( #[cfg(test)] mod tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn cahaba_ap() { let result = calc_ptf_puckett1985(70.9f64, 36.4f64, 11.8f64, 1.67f64, 0.38f64); - assert_close(result.theta_0, 0.34288f64, 0.000000000001f64, 0.00000001f64); - assert_close(result.theta_1, 0.33926f64, 0.000000000001f64, 0.00000001f64); + assert_close( + result.theta_0, + 0.34288f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", + ); + assert_close( + result.theta_1, + 0.33926f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", + ); assert_close( result.theta_5, 0.3938615f64, - 0.000000000001f64, - 0.00000001f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", ); assert_close( result.theta_10, 0.39330438f64, - 0.000000000001f64, - 0.00000001f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", ); assert_close( result.theta_30, 0.34432936f64, - 0.000000000001f64, - 0.00000001f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", ); assert_close( result.theta_60, 0.31153562f64, - 0.000000000001f64, - 0.00000001f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", ); assert_close( result.theta_100, 0.29292896f64, - 0.000000000001f64, - 0.00000001f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", ); assert_close( result.theta_500, 0.2513588f64, - 0.000000000001f64, - 0.00000001f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", ); assert_close( result.theta_1000, 0.25187788f64, - 0.000000000001f64, - 0.00000001f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", ); assert_close( result.theta_1500, 0.22746346f64, - 0.000000000001f64, - 0.00000001f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", ); assert_close( result.k_sat, 0.0000042399741f64, - 0.000000000001f64, - 0.00000001f64, + 0.0000000001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "meter_per_second", + "registry", ); } } diff --git a/targets/ptfkit-rs/src/rawls1982.rs b/targets/ptfkit-rs/src/rawls1982.rs index 9be9392..c396781 100644 --- a/targets/ptfkit-rs/src/rawls1982.rs +++ b/targets/ptfkit-rs/src/rawls1982.rs @@ -15,6 +15,41 @@ Agricultural soils from 32 states of the USA 1,323 soils with about 5,350 horizons; regression equations used 2,541 horizons."] +#[cfg(test)] +fn resolved_tolerance(expected: f64, absolute: f64, relative: f64) -> f64 { + absolute + .max(relative * expected.abs()) + .max(0.00000000000001f64) +} +#[cfg(test)] +fn assert_close( + actual: f64, + expected: f64, + absolute: f64, + relative: f64, + quantity: &str, + unit: &str, + source: &str, +) { + let difference = (actual - expected).abs(); + let tolerance = resolved_tolerance(expected, absolute, relative); + assert!( + difference <= tolerance, + "actual={actual}, expected={expected}, difference={difference}, tolerance={tolerance}, quantity={quantity}, unit={unit}, source={source}" + ); +} +#[cfg(test)] +mod comparator_tests { + use super::*; + #[test] + fn accepts_below_and_rejects_above_tolerance() { + for expected in [0.0, 2.0, -2.0] { + let tolerance = resolved_tolerance(expected, 0.001, 0.01); + assert!((expected + tolerance * 0.5 - expected).abs() <= tolerance); + assert!((expected + tolerance * 2.0 - expected).abs() > tolerance); + } + } +} #[doc = r"Estimate volumetric water content at -1500 kPa. # Arguments @@ -41,16 +76,18 @@ pub fn calc_ptf_rawls1982_theta_1500(clay: f64, organic_matter: f64) -> f64 { #[cfg(test)] mod calc_ptf_rawls1982_theta_1500_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn loamy_sand() { let result = calc_ptf_rawls1982_theta_1500(5.12f64, 0.1f64); - assert_close(result, 0.05318f64, 0.000000000001f64, 0.0000000001f64); + assert_close( + result, + 0.05318f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", + ); } } #[doc = r"Estimate volumetric water content at -33 kPa using measured or estimated theta_1500. @@ -80,16 +117,18 @@ pub fn calc_ptf_rawls1982_theta_33(sand: f64, organic_matter: f64, theta_1500: f #[cfg(test)] mod calc_ptf_rawls1982_theta_33_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn loamy_sand_with_estimated_theta_1500() { let result = calc_ptf_rawls1982_theta_33(85f64, 0.1f64, 0.05318f64); - assert_close(result, 0.1179896f64, 0.000000000001f64, 0.0000000001f64); + assert_close( + result, + 0.1179896f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", + ); } } #[doc = r"Results returned by `calc_ptf_rawls1982_full_wrc`."] @@ -193,86 +232,116 @@ pub fn calc_ptf_rawls1982_full_wrc( #[cfg(test)] mod calc_ptf_rawls1982_full_wrc_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn loamy_sand() { let result = calc_ptf_rawls1982_full_wrc(85f64, 0.66f64, 1.22f64, 0.091f64, 0.033f64); assert_close( result.theta_4, 0.247242f64, - 0.000000000001f64, - 0.0000000001f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", ); assert_close( result.theta_7, 0.968738f64, - 0.000000000001f64, - 0.0000000001f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", ); assert_close( result.theta_10, 0.145588f64, - 0.000000000001f64, - 0.0000000001f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", ); assert_close( result.theta_20, 0.10483f64, - 0.000000000001f64, - 0.0000000001f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", ); assert_close( result.theta_33, 0.091f64, - 0.000000000001f64, - 0.0000000001f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", ); assert_close( result.theta_60, 0.075428f64, - 0.000000000001f64, - 0.0000000001f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", ); assert_close( result.theta_100, 0.063192f64, - 0.000000000001f64, - 0.0000000001f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", ); assert_close( result.theta_200, 0.052946f64, - 0.000000000001f64, - 0.0000000001f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", ); assert_close( result.theta_400, 0.045826f64, - 0.000000000001f64, - 0.0000000001f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", ); assert_close( result.theta_700, 0.041824f64, - 0.000000000001f64, - 0.0000000001f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", ); assert_close( result.theta_1000, 0.038932f64, - 0.000000000001f64, - 0.0000000001f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", ); assert_close( result.theta_1500, 0.033f64, - 0.000000000001f64, - 0.0000000001f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", ); } } diff --git a/targets/ptfkit-rs/src/saxton2006.rs b/targets/ptfkit-rs/src/saxton2006.rs index f464a3a..22d03ac 100644 --- a/targets/ptfkit-rs/src/saxton2006.rs +++ b/targets/ptfkit-rs/src/saxton2006.rs @@ -17,6 +17,41 @@ United States 1,722 mineral-soil A-horizon samples retained from the USDA/NRCS National Soil Characterization database after exclusions from 2,149 samples."] +#[cfg(test)] +fn resolved_tolerance(expected: f64, absolute: f64, relative: f64) -> f64 { + absolute + .max(relative * expected.abs()) + .max(0.00000000000001f64) +} +#[cfg(test)] +fn assert_close( + actual: f64, + expected: f64, + absolute: f64, + relative: f64, + quantity: &str, + unit: &str, + source: &str, +) { + let difference = (actual - expected).abs(); + let tolerance = resolved_tolerance(expected, absolute, relative); + assert!( + difference <= tolerance, + "actual={actual}, expected={expected}, difference={difference}, tolerance={tolerance}, quantity={quantity}, unit={unit}, source={source}" + ); +} +#[cfg(test)] +mod comparator_tests { + use super::*; + #[test] + fn accepts_below_and_rejects_above_tolerance() { + for expected in [0.0, 2.0, -2.0] { + let tolerance = resolved_tolerance(expected, 0.001, 0.01); + assert!((expected + tolerance * 0.5 - expected).abs() <= tolerance); + assert!((expected + tolerance * 2.0 - expected).abs() > tolerance); + } + } +} #[doc = r"Results returned by `calc_ptf_saxton2006`."] #[derive(Clone, Copy, Debug, PartialEq)] pub struct Saxton2006PTFResult { @@ -135,74 +170,98 @@ pub fn calc_ptf_saxton2006(sand: f64, clay: f64, organic_matter: f64) -> Saxton2 #[cfg(test)] mod calc_ptf_saxton2006_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn table_3_sand() { let result = calc_ptf_saxton2006(0.88f64, 0.05f64, 2.5f64); assert_close( result.theta_1500, 0.05022058f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", ); assert_close( result.theta_33, 0.10282792364858702f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", ); assert_close( result.theta_s, 0.46172240764858724f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", ); assert_close( result.plant_available_water, 0.05260734364858702f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", ); assert_close( result.air_entry_tension, 0.5986789729513293f64, - 0.000000000001f64, - 0.000000000001f64, + 0.01f64, + 0f64, + "matric_potential", + "kilopascal", + "registry", ); assert_close( result.retention_a, 0.00018076452552206025f64, - 0.000000000001f64, - 0.000000000001f64, + 0.000000001f64, + 0.01f64, + "retention_coefficient_a", + "kilopascal", + "registry", ); assert_close( result.retention_b, 5.325903100453899f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "retention_coefficient_b", + "dimensionless", + "registry", ); assert_close( result.conductivity_lambda, 0.18776158355467926f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "conductivity_shape_parameter", + "dimensionless", + "registry", ); assert_close( result.saturated_conductivity, 108.1478278507403f64, - 0.000000000001f64, - 0.000000000001f64, + 0.0001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "millimeter_per_hour", + "registry", ); assert_close( result.normal_density, 1.4264356197312436f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "dry_bulk_density", + "gram_per_cubic_centimeter", + "registry", ); } } @@ -265,12 +324,6 @@ pub fn calc_ptf_saxton2006_density( #[cfg(test)] mod calc_ptf_saxton2006_density_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn table_3_sand_compacted() { let result = calc_ptf_saxton2006_density( @@ -282,26 +335,38 @@ mod calc_ptf_saxton2006_density_tests { assert_close( result.adjusted_density, 1.569079181704368f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "dry_bulk_density", + "gram_per_cubic_centimeter", + "registry", ); assert_close( result.adjusted_theta_s, 0.407894648413446f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", ); assert_close( result.adjusted_theta_33, 0.09206237180155877f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", ); assert_close( result.adjusted_theta_s_minus_33, 0.3158322766118873f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", ); } } @@ -340,12 +405,6 @@ pub fn calc_ptf_saxton2006_tension_dry(theta: f64, theta_1500: f64, theta_33: f6 #[cfg(test)] mod calc_ptf_saxton2006_tension_dry_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn table_3_sand_midpoint() { let result = calc_ptf_saxton2006_tension_dry( @@ -356,8 +415,11 @@ mod calc_ptf_saxton2006_tension_dry_tests { assert_close( result, 159.18094591362183f64, - 0.000000000001f64, - 0.000000000001f64, + 0.01f64, + 0f64, + "matric_potential", + "kilopascal", + "registry", ); } } @@ -399,12 +461,6 @@ pub fn calc_ptf_saxton2006_tension_wet( #[cfg(test)] mod calc_ptf_saxton2006_tension_wet_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn table_3_sand_theta_0_2() { let result = calc_ptf_saxton2006_tension_wet( @@ -416,8 +472,11 @@ mod calc_ptf_saxton2006_tension_wet_tests { assert_close( result, 24.227216407352152f64, - 0.000000000001f64, - 0.000000000001f64, + 0.01f64, + 0f64, + "matric_potential", + "kilopascal", + "registry", ); } } @@ -458,12 +517,6 @@ pub fn calc_ptf_saxton2006_conductivity( #[cfg(test)] mod calc_ptf_saxton2006_conductivity_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn table_3_sand_theta_0_3() { let result = calc_ptf_saxton2006_conductivity( @@ -475,8 +528,11 @@ mod calc_ptf_saxton2006_conductivity_tests { assert_close( result, 0.3003203142764693f64, - 0.000000000001f64, - 0.000000000001f64, + 0.0001f64, + 0.01f64, + "unsaturated_hydraulic_conductivity", + "millimeter_per_hour", + "registry", ); } } @@ -546,12 +602,6 @@ pub fn calc_ptf_saxton2006_gravel( #[cfg(test)] mod calc_ptf_saxton2006_gravel_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn table_3_sand_twenty_percent_gravel() { let result = calc_ptf_saxton2006_gravel( @@ -563,26 +613,38 @@ mod calc_ptf_saxton2006_gravel_tests { assert_close( result.gravel_volume_fraction, 0.11860834455314037f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "gravel_volume_fraction", + "volume_fraction", + "registry", ); assert_close( result.bulk_density, 1.5715605653291098f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "dry_bulk_density", + "gram_per_cubic_centimeter", + "registry", ); assert_close( result.bulk_plant_available_water, 0.04636767370708995f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", ); assert_close( result.bulk_saturated_conductivity, 97.54880510583706f64, - 0.000000000001f64, - 0.000000000001f64, + 0.0001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "millimeter_per_hour", + "registry", ); } } @@ -636,26 +698,26 @@ pub fn calc_ptf_saxton2006_salinity( #[cfg(test)] mod calc_ptf_saxton2006_salinity_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn saline_partly_saturated_soil() { let result = calc_ptf_saxton2006_salinity(4f64, 0.3f64, 0.46172240764858724f64); assert_close( result.saturated_osmotic_potential, 144f64, - 0.000000000001f64, - 0.000000000001f64, + 0.01f64, + 0f64, + "osmotic_potential", + "kilopascal", + "registry", ); assert_close( result.osmotic_potential, 221.6267556713219f64, - 0.000000000001f64, - 0.000000000001f64, + 0.01f64, + 0f64, + "osmotic_potential", + "kilopascal", + "registry", ); } } diff --git a/targets/ptfkit-rs/src/tiwary2014.rs b/targets/ptfkit-rs/src/tiwary2014.rs index 5582a8a..0c1880d 100644 --- a/targets/ptfkit-rs/src/tiwary2014.rs +++ b/targets/ptfkit-rs/src/tiwary2014.rs @@ -17,6 +17,41 @@ Indo-Gangetic Plains and black soil region of India 209 layers from 30 Indo-Gangetic Plains profiles and 275 layers from 62 black-soil profiles; equation-specific subsets are described below."] +#[cfg(test)] +fn resolved_tolerance(expected: f64, absolute: f64, relative: f64) -> f64 { + absolute + .max(relative * expected.abs()) + .max(0.00000000000001f64) +} +#[cfg(test)] +fn assert_close( + actual: f64, + expected: f64, + absolute: f64, + relative: f64, + quantity: &str, + unit: &str, + source: &str, +) { + let difference = (actual - expected).abs(); + let tolerance = resolved_tolerance(expected, absolute, relative); + assert!( + difference <= tolerance, + "actual={actual}, expected={expected}, difference={difference}, tolerance={tolerance}, quantity={quantity}, unit={unit}, source={source}" + ); +} +#[cfg(test)] +mod comparator_tests { + use super::*; + #[test] + fn accepts_below_and_rejects_above_tolerance() { + for expected in [0.0, 2.0, -2.0] { + let tolerance = resolved_tolerance(expected, 0.001, 0.01); + assert!((expected + tolerance * 0.5 - expected).abs() <= tolerance); + assert!((expected + tolerance * 2.0 - expected).abs() > tolerance); + } + } +} #[doc = r"Estimate saturated conductivity for Indo-Gangetic Plains soils. # Arguments @@ -51,20 +86,17 @@ pub fn calc_ptf_tiwary2014_igp(sand: f64, bulk_density: f64, esp: f64) -> f64 { #[cfg(test)] mod calc_ptf_tiwary2014_igp_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn igp_saturated_conductivity_case() { let result = calc_ptf_tiwary2014_igp(37.3f64, 1.674f64, 4.6f64); assert_close( result, 0.0000005103578f64, - 0.000000000001f64, 0.0000000001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "meter_per_second", + "registry", ); } } @@ -131,28 +163,44 @@ pub fn calc_ptf_tiwary2014_bsr( #[cfg(test)] mod calc_ptf_tiwary2014_bsr_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn black_soil_compatibility_case() { let result = calc_ptf_tiwary2014_bsr(54.9f64, 7.6f64, 61.6f64, 7.3f64, 21.4f64, 3.32f64); - assert_close(result.w_33, 41.1729f64, 0.000000000001f64, 0.0000000001f64); - assert_close(result.w_100, 36.8273f64, 0.000000000001f64, 0.0000000001f64); + assert_close( + result.w_33, + 41.1729f64, + 0.1f64, + 0f64, + "gravimetric_water_content", + "mass_percent", + "registry", + ); + assert_close( + result.w_100, + 36.8273f64, + 0.1f64, + 0f64, + "gravimetric_water_content", + "mass_percent", + "registry", + ); assert_close( result.w_1500, 21.6976f64, - 0.000000000001f64, - 0.0000000001f64, + 0.1f64, + 0f64, + "gravimetric_water_content", + "mass_percent", + "registry", ); assert_close( result.k_sat, 0.000005373367f64, - 0.000000000001f64, 0.0000000001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "meter_per_second", + "registry", ); } } diff --git a/targets/ptfkit-rs/src/varallyai1982.rs b/targets/ptfkit-rs/src/varallyai1982.rs index da3bdf9..9779a40 100644 --- a/targets/ptfkit-rs/src/varallyai1982.rs +++ b/targets/ptfkit-rs/src/varallyai1982.rs @@ -17,6 +17,41 @@ Hungary, mainly the Hungarian Plain Undisturbed samples from 559 genetic horizons in 160 soil profiles; the meadow-series regressions used 68 samples and the chernozem comparison used 108 samples."] +#[cfg(test)] +fn resolved_tolerance(expected: f64, absolute: f64, relative: f64) -> f64 { + absolute + .max(relative * expected.abs()) + .max(0.00000000000001f64) +} +#[cfg(test)] +fn assert_close( + actual: f64, + expected: f64, + absolute: f64, + relative: f64, + quantity: &str, + unit: &str, + source: &str, +) { + let difference = (actual - expected).abs(); + let tolerance = resolved_tolerance(expected, absolute, relative); + assert!( + difference <= tolerance, + "actual={actual}, expected={expected}, difference={difference}, tolerance={tolerance}, quantity={quantity}, unit={unit}, source={source}" + ); +} +#[cfg(test)] +mod comparator_tests { + use super::*; + #[test] + fn accepts_below_and_rejects_above_tolerance() { + for expected in [0.0, 2.0, -2.0] { + let tolerance = resolved_tolerance(expected, 0.001, 0.01); + assert!((expected + tolerance * 0.5 - expected).abs() <= tolerance); + assert!((expected + tolerance * 2.0 - expected).abs() > tolerance); + } + } +} #[doc = r"Results returned by `calc_ptf_varallyai1982_meadow`."] #[derive(Clone, Copy, Debug, PartialEq)] pub struct Varallyai1982Parameters { @@ -82,27 +117,35 @@ pub fn calc_ptf_varallyai1982_meadow( #[cfg(test)] mod calc_ptf_varallyai1982_meadow_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn representative_review_case() { let result = calc_ptf_varallyai1982_meadow(1.4f64, 0.3f64, 0.25f64); assert_close( result.theta_0, 46.54495f64, - 0.000000000001f64, - 0.000000000001f64, + 0.1f64, + 0f64, + "volumetric_water_content", + "volume_percent", + "registry", + ); + assert_close( + result.m, + 0.10029f64, + 0.001f64, + 0f64, + "varallyai_m", + "dimensionless", + "registry", ); - assert_close(result.m, 0.10029f64, 0.000000000001f64, 0.000000000001f64); assert_close( result.pf_star, 3.62445f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "varallyai_pf_star", + "dimensionless", + "registry", ); } } @@ -156,27 +199,35 @@ pub fn calc_ptf_varallyai1982_chernozem_a( #[cfg(test)] mod calc_ptf_varallyai1982_chernozem_a_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn representative_review_case() { let result = calc_ptf_varallyai1982_chernozem_a(1.4f64, 0.35f64); assert_close( result.theta_0, 52.005f64, - 0.000000000001f64, - 0.000000000001f64, + 0.1f64, + 0f64, + "volumetric_water_content", + "volume_percent", + "registry", + ); + assert_close( + result.m, + 0.4174f64, + 0.001f64, + 0f64, + "varallyai_m", + "dimensionless", + "registry", ); - assert_close(result.m, 0.4174f64, 0.000000000001f64, 0.000000000001f64); assert_close( result.pf_star, 4.00469f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "varallyai_pf_star", + "dimensionless", + "registry", ); } } @@ -230,27 +281,35 @@ pub fn calc_ptf_varallyai1982_chernozem_b( #[cfg(test)] mod calc_ptf_varallyai1982_chernozem_b_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn representative_review_case() { let result = calc_ptf_varallyai1982_chernozem_b(1.4f64, 0.35f64); assert_close( result.theta_0, 47.60035f64, - 0.000000000001f64, - 0.000000000001f64, + 0.1f64, + 0f64, + "volumetric_water_content", + "volume_percent", + "registry", + ); + assert_close( + result.m, + 0.407f64, + 0.001f64, + 0f64, + "varallyai_m", + "dimensionless", + "registry", ); - assert_close(result.m, 0.407f64, 0.000000000001f64, 0.000000000001f64); assert_close( result.pf_star, 3.9299f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "varallyai_pf_star", + "dimensionless", + "registry", ); } } @@ -305,27 +364,35 @@ pub fn calc_ptf_varallyai1982_chernozem_c( #[cfg(test)] mod calc_ptf_varallyai1982_chernozem_c_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn representative_review_case() { let result = calc_ptf_varallyai1982_chernozem_c(1.4f64, 0.35f64); assert_close( result.theta_0, 49.87f64, - 0.000000000001f64, - 0.000000000001f64, + 0.1f64, + 0f64, + "volumetric_water_content", + "volume_percent", + "registry", + ); + assert_close( + result.m, + 0.84011f64, + 0.001f64, + 0f64, + "varallyai_m", + "dimensionless", + "registry", ); - assert_close(result.m, 0.84011f64, 0.000000000001f64, 0.000000000001f64); assert_close( result.pf_star, 3.59772f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "varallyai_pf_star", + "dimensionless", + "registry", ); } } diff --git a/targets/ptfkit-rs/src/vereecken1989.rs b/targets/ptfkit-rs/src/vereecken1989.rs index 72cb70a..49d7841 100644 --- a/targets/ptfkit-rs/src/vereecken1989.rs +++ b/targets/ptfkit-rs/src/vereecken1989.rs @@ -17,6 +17,41 @@ Belgian territory north of the river axis Samber and Meuse 182 horizons from 40 important Belgian soil series, with textures ranging from sand to heavy clay."] +#[cfg(test)] +fn resolved_tolerance(expected: f64, absolute: f64, relative: f64) -> f64 { + absolute + .max(relative * expected.abs()) + .max(0.00000000000001f64) +} +#[cfg(test)] +fn assert_close( + actual: f64, + expected: f64, + absolute: f64, + relative: f64, + quantity: &str, + unit: &str, + source: &str, +) { + let difference = (actual - expected).abs(); + let tolerance = resolved_tolerance(expected, absolute, relative); + assert!( + difference <= tolerance, + "actual={actual}, expected={expected}, difference={difference}, tolerance={tolerance}, quantity={quantity}, unit={unit}, source={source}" + ); +} +#[cfg(test)] +mod comparator_tests { + use super::*; + #[test] + fn accepts_below_and_rejects_above_tolerance() { + for expected in [0.0, 2.0, -2.0] { + let tolerance = resolved_tolerance(expected, 0.001, 0.01); + assert!((expected + tolerance * 0.5 - expected).abs() <= tolerance); + assert!((expected + tolerance * 2.0 - expected).abs() > tolerance); + } + } +} #[doc = r"Results returned by `calc_ptf_vereecken1989`."] #[derive(Clone, Copy, Debug, PartialEq)] pub struct Vereecken1989PTFResult { @@ -86,38 +121,44 @@ pub fn calc_ptf_vereecken1989( #[cfg(test)] mod calc_ptf_vereecken1989_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn dataset_mean_properties() { let result = calc_ptf_vereecken1989(52.14f64, 10.93f64, 1.03f64, 1.466f64); assert_close( result.theta_r, 0.08406999999999999f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", ); assert_close( result.theta_s, 0.4060520000000001f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", ); assert_close( result.alpha, 0.003581613076579849f64, - 0.000000000001f64, - 0.000000000001f64, + 0.00001f64, + 0f64, + "van_genuchten_alpha", + "inverse_centimeter", + "registry", ); assert_close( result.n, 0.8602234826041976f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "van_genuchten_n", + "dimensionless", + "registry", ); } } @@ -245,12 +286,6 @@ pub fn calc_ptf_vereecken1989_detailed( #[cfg(test)] mod calc_ptf_vereecken1989_detailed_tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn dataset_mean_properties() { let result = calc_ptf_vereecken1989_detailed( @@ -260,26 +295,38 @@ mod calc_ptf_vereecken1989_detailed_tests { assert_close( result.theta_r, 0.08277820000000001f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", ); assert_close( result.theta_s, 0.4060520000000001f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", ); assert_close( result.alpha, 0.02522115884641546f64, - 0.000000000001f64, - 0.000000000001f64, + 0.00001f64, + 0f64, + "van_genuchten_alpha", + "inverse_centimeter", + "registry", ); assert_close( result.n, 0.517175605329397f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "van_genuchten_n", + "dimensionless", + "registry", ); } } diff --git a/targets/ptfkit-rs/src/wang2012.rs b/targets/ptfkit-rs/src/wang2012.rs index cf6eb20..5198245 100644 --- a/targets/ptfkit-rs/src/wang2012.rs +++ b/targets/ptfkit-rs/src/wang2012.rs @@ -16,6 +16,41 @@ Surface soils across the entire Loess Plateau, China 382 surface (0-5 cm) sites; 252 derivation and 130 validation data sets."] +#[cfg(test)] +fn resolved_tolerance(expected: f64, absolute: f64, relative: f64) -> f64 { + absolute + .max(relative * expected.abs()) + .max(0.00000000000001f64) +} +#[cfg(test)] +fn assert_close( + actual: f64, + expected: f64, + absolute: f64, + relative: f64, + quantity: &str, + unit: &str, + source: &str, +) { + let difference = (actual - expected).abs(); + let tolerance = resolved_tolerance(expected, absolute, relative); + assert!( + difference <= tolerance, + "actual={actual}, expected={expected}, difference={difference}, tolerance={tolerance}, quantity={quantity}, unit={unit}, source={source}" + ); +} +#[cfg(test)] +mod comparator_tests { + use super::*; + #[test] + fn accepts_below_and_rejects_above_tolerance() { + for expected in [0.0, 2.0, -2.0] { + let tolerance = resolved_tolerance(expected, 0.001, 0.01); + assert!((expected + tolerance * 0.5 - expected).abs() <= tolerance); + assert!((expected + tolerance * 2.0 - expected).abs() > tolerance); + } + } +} #[doc = r"Results returned by `calc_ptf_wang2012`."] #[derive(Clone, Copy, Debug, PartialEq)] pub struct Wang2012PTFResult { @@ -103,32 +138,35 @@ pub fn calc_ptf_wang2012( #[cfg(test)] mod tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn derivation_minimum_soc_case() { let result = calc_ptf_wang2012(85f64, 10f64, 5f64, 1.22f64, 0.033f64, 1193f64); assert_close( result.theta_s, 0.61540575f64, - 0.000000000001f64, - 0.000001f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", ); assert_close( result.theta_fc, 0.38491949f64, - 0.000000000001f64, - 0.000001f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", ); assert_close( result.k_sat, 0.00003872974f64, - 0.000000000001f64, - 0.000001f64, + 0.0000000001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "meter_per_second", + "registry", ); } } diff --git a/targets/ptfkit-rs/src/weber2020.rs b/targets/ptfkit-rs/src/weber2020.rs index 35babab..67ed82a 100644 --- a/targets/ptfkit-rs/src/weber2020.rs +++ b/targets/ptfkit-rs/src/weber2020.rs @@ -19,6 +19,41 @@ A compilation of 1,729 samples with water-retention and hydraulic-conductivity d data set DS1 contained 392 of the 402 retained samples, DS2 was used for tau, DS3 contained 359 samples for K_snc, and 10 samples were held out as DS4."] +#[cfg(test)] +fn resolved_tolerance(expected: f64, absolute: f64, relative: f64) -> f64 { + absolute + .max(relative * expected.abs()) + .max(0.00000000000001f64) +} +#[cfg(test)] +fn assert_close( + actual: f64, + expected: f64, + absolute: f64, + relative: f64, + quantity: &str, + unit: &str, + source: &str, +) { + let difference = (actual - expected).abs(); + let tolerance = resolved_tolerance(expected, absolute, relative); + assert!( + difference <= tolerance, + "actual={actual}, expected={expected}, difference={difference}, tolerance={tolerance}, quantity={quantity}, unit={unit}, source={source}" + ); +} +#[cfg(test)] +mod comparator_tests { + use super::*; + #[test] + fn accepts_below_and_rejects_above_tolerance() { + for expected in [0.0, 2.0, -2.0] { + let tolerance = resolved_tolerance(expected, 0.001, 0.01); + assert!((expected + tolerance * 0.5 - expected).abs() <= tolerance); + assert!((expected + tolerance * 2.0 - expected).abs() > tolerance); + } + } +} #[doc = r"Results returned by `calc_ptf_weber2020`."] #[derive(Clone, Copy, Debug, PartialEq)] pub struct Weber2020PTFResult { @@ -103,56 +138,71 @@ pub fn calc_ptf_weber2020( #[cfg(test)] mod tests { use super::*; - fn assert_close(actual: f64, expected: f64, atol: f64, rtol: f64) { - assert!( - (actual - expected).abs() <= atol + rtol * expected.abs(), - "actual {actual} != expected {expected}" - ); - } #[test] fn representative_vgm_parameters() { let result = calc_ptf_weber2020(0.05f64, 0.45f64, 0.02f64, 1.6f64, -0.5f64, 100f64); assert_close( result.theta_snc_bw, 0.06267f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", ); assert_close( result.theta_sc_bw, 0.38607f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "volumetric_water_content", + "volume_fraction", + "registry", ); assert_close( result.alpha_bw, 0.0201472407335197f64, - 0.000000000001f64, - 0.000000000001f64, + 0.00001f64, + 0f64, + "van_genuchten_alpha", + "inverse_centimeter", + "registry", ); assert_close( result.n_bw, 1.71980542683289f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "brunswick_n", + "dimensionless", + "registry", ); assert_close( result.tau_bw, -0.887f64, - 0.000000000001f64, - 0.000000000001f64, + 0.001f64, + 0f64, + "brunswick_tau", + "dimensionless", + "registry", ); assert_close( result.k_sc_bw, 172.186857498601f64, - 0.000000000001f64, - 0.000000000001f64, + 0.0001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "centimeter_per_day", + "registry", ); assert_close( result.k_snc_bw, 0.0190546071796325f64, - 0.000000000001f64, - 0.000000000001f64, + 0.0001f64, + 0.01f64, + "saturated_hydraulic_conductivity", + "centimeter_per_day", + "registry", ); } }