diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 0bbd026..ee6e74b 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -8,11 +8,11 @@ - [ ] I performed a human privacy review and found no private project names, paths, source text, inputs, or results. - [ ] I confirmed that all included material may be redistributed. - [ ] I classified each expectation as reference-backed, stability-baseline, or health-only. -- [ ] I independently verified reference-backed assertions without relying solely on Graphcal output. +- [ ] I mapped every reference-backed expected value directly to a value reported by a reliable public source. - [ ] I recorded producing revisions, comparison rules, and repeated-run determinism for stability baselines. - [ ] I recorded candidate-generation and human-review provenance. - [ ] I ran `uv run validate-corpus` and `uv run -m pytest`. ## Evidence - + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 24d2ef4..9df21ab 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,14 +1,16 @@ # Contributing to the Graphcal QA Corpus -Contributions are welcome when they add independently traceable correctness evidence or original, realistic Graphcal complexity without exposing private material. +Contributions are welcome when they add directly source-traceable correctness evidence or original, realistic Graphcal complexity without exposing private material. ## Candidate requirements A candidate must be either: -1. a **source-backed reproduction** reimplemented from a reliable, redistributable published or well-known analysis; or +1. a **source-backed reproduction** for which a reliable, redistributable public source supplies both the reproduced inputs and the expected output values; or 2. an **original synthetic complexity project** created without copying or transforming a private user project. +A project that applies a published equation to original or adapted inputs is original synthetic unless the source also reports the corresponding output. Such a case may cite the equation, but its Graphcal-produced output must be classified as a stability baseline. + Do not submit a candidate merely because the current Graphcal implementation accepts it. Each project must answer a stated engineering question, exercise a documented feature or domain gap, and use deterministic checked-in inputs. Initial corpus projects must not require network access, private or unpinned dependencies, native or WASM plugins, current time, randomness, or project-provided executable hooks. @@ -19,7 +21,7 @@ Initial corpus projects must not require network access, private or unpinned dep 2. **Perform privacy review.** Apply [PRIVACY.md](PRIVACY.md) to names, paths, comments, source, inputs, expected outputs, references, and history. Agent generation and automated scanning do not replace human inspection. 3. **Check structure.** Put the project at `projects//`, add `graphcal.toml`, declare the project and all QA cases in `corpus.toml`, and run `uv run validate-corpus`. 4. **Classify every expectation.** Use `reference-backed`, `stability-baseline`, or `health-only` according to the evidence described below. Do not present stability output as a correctness oracle. -5. **Verify evidence.** Independently check reference-backed assertions. Capture provenance and repeated-run determinism for stability baselines. +5. **Verify evidence.** Trace every reference-backed expected value to the exact public source location that reports it. Capture producing revisions and repeated-run determinism for stability baselines. 6. **Submit a pull request.** Explain the project's origin, purpose, coverage, evidence, licensing, and privacy review. 7. **Obtain human review.** At least one human other than the latest contributor must review the structural, technical, provenance, expectation, and privacy evidence before merge. @@ -29,20 +31,20 @@ Coding-agent output remains an untrusted candidate until this process is complet ### Reference-backed -A reference-backed assertion must be supported by at least one of: +A reference-backed assertion requires a reliable public source that reports both: + +- the inputs and assumptions reproduced by the case; and +- the expected numerical or scalar output asserted by the manifest. -- an analytical calculation; -- a separately implemented reference calculation; -- a reliable published example with compatible assumptions; or -- manual verification by a domain expert. +The evidence must map each JSON Pointer and expected value to an exact source location such as a worked example, report table, published test vector, or source-maintained reference output. Assert only the precision the source reports. Document any source-prescribed rounding and any lossless unit or representation conversion. -Do not bless a value solely because `graphcal eval` produced it. Document formulas, assumptions, constants, sign and unit conventions, copied input data, adaptations, tolerances, and the independent result in `reference/calculation.md` or an equivalent non-executable record. +An analytical calculation, separately implemented calculation, domain-expert review, or agreement with `graphcal eval` may corroborate a published value, but none is sufficient as the sole oracle. If a source publishes an equation but no output for the case's inputs, classify the case as `stability-baseline`, not `reference-backed`. -For a published source, include enough bibliographic detail to identify the exact example: author or organization, title, edition or version, page or section, DOI or stable URL when available, and access date where useful. Reimplement the analysis; do not copy copyrighted prose, figures, or substantial tables. Record the license and origin of any included third-party data. +Include enough bibliographic detail to identify the exact example: author or organization, title, edition or version, page, table, equation, or section, DOI or stable URL when available, and access date where useful. Reimplement the analysis; do not copy copyrighted prose, figures, or substantial tables. Record the license and origin of any included third-party data. ### Stability baseline -A stability baseline detects behavioral drift and does not establish engineering correctness. Record: +A stability baseline detects behavioral drift and does not establish engineering correctness. Use it for original synthetic cases and source-informed cases whose references provide equations or methods but no expected values for the reproduced inputs. Record: - the exact Graphcal commit that produced it; - the corpus candidate commit and case inputs; @@ -78,7 +80,7 @@ A project pull request must confirm that: - [ ] all included source material and data may be redistributed; - [ ] the project is explicitly declared and structural validation passes; - [ ] every expectation has the correct evidence class; -- [ ] every reference-backed assertion was independently verified; +- [ ] every reference-backed assertion maps directly to a value reported by a reliable public source; - [ ] every stability baseline records producing revisions, comparison rules, and repeated-run determinism; - [ ] generation and human review provenance are documented; and - [ ] the project requires no network, plugin, randomness, clock, private dependency, or executable project hook. diff --git a/README.md b/README.md index 5f6ed02..9cf0a0d 100644 --- a/README.md +++ b/README.md @@ -6,16 +6,16 @@ Every project has one of two origins: -- **Source-backed reproduction:** a reimplementation of a reliable published or well-known engineering analysis. Independently verified assertions may make correctness claims. Citations, assumptions, adaptations, and redistributable inputs must be recorded. -- **Original synthetic complexity:** a new, imaginary engineering project designed to exercise realistic combinations of Graphcal features. Graphcal-produced expected output is a stability baseline, not proof that the engineering result is correct. +- **Source-backed reproduction:** a reliable public source supplies both the reproduced inputs and the correct output values. Reference-backed assertions must trace directly to values reported by that source, including any explicit source rounding or documented unit conversion. +- **Original synthetic complexity:** a new scenario designed to exercise realistic combinations of Graphcal features. This includes source-informed models that reuse a published equation with original or adapted inputs when the source does not report the corresponding outputs. Graphcal-produced expected output is a stability baseline, not proof that the engineering result is correct. -A project may combine independently verified reference assertions with stability baselines for other outputs. Each expectation must state which class it belongs to. +A published equation alone does not make a case source-backed. A local analytical calculation, separate implementation, or domain review may corroborate a source value, but it cannot be the sole oracle for a reference-backed assertion. Each case has one expectation class; a project with multiple entrypoints may use different classes only when each case independently satisfies its evidence contract. Private project names, paths, source text, inputs, outputs, and results are prohibited, even when edited or anonymized. See [PRIVACY.md](PRIVACY.md). ## Trust model -Coding agents and other generators may prepare **candidates**, but generated content is untrusted. A human promotes a candidate only after reviewing its privacy, structure, provenance, expectation classification, independent calculations, and determinism evidence. The complete process is in [CONTRIBUTING.md](CONTRIBUTING.md). +Coding agents and other generators may prepare **candidates**, but generated content is untrusted. A human promotes a candidate only after reviewing its privacy, structure, provenance, expectation classification, source-to-assertion traceability, and determinism evidence. The complete process is in [CONTRIBUTING.md](CONTRIBUTING.md). Corpus projects are data-only and must not contain executable test hooks. This repository provides an opt-in local runner for a caller-supplied Graphcal executable; it never invokes project-provided programs. Repository CI performs structural validation only and does not run Graphcal. diff --git a/corpus.toml b/corpus.toml index 4dd83ac..39a7023 100644 --- a/corpus.toml +++ b/corpus.toml @@ -55,3 +55,235 @@ evidence = "reference/calculation.md" pointer = "/node/mass_ratio/si_value" expected = 10.0 absolute_tolerance = 1.0 +[[projects]] +id = "nasa-lift-sweep" +path = "projects/nasa-lift-sweep" +status = "active" +domain = "aerodynamics" +origin = "original-synthetic" +purpose = "Detect semantic drift in a source-informed lift sweep with original inputs." + +[[projects.cases]] +id = "three-flight-points" +entry = "src/nasa_lift_sweep/main.gcl" + +[projects.cases.expectation] +kind = "stability-baseline" +expected = "expected/three-flight-points.json" +evidence = "reference/baseline.md" + +[projects.cases.expectation.comparison] +mode = "semantic-json" +tolerances = [] + +[[projects]] +id = "pvwatts-hourly-dc-energy" +path = "projects/pvwatts-hourly-dc-energy" +status = "active" +domain = "solar-energy" +origin = "original-synthetic" +purpose = "Detect semantic drift in a source-informed PVWatts profile with original inputs." + +[[projects.cases]] +id = "four-hour-profile" +entry = "src/pvwatts_hourly_dc_energy/main.gcl" + +[projects.cases.expectation] +kind = "stability-baseline" +expected = "expected/four-hour-profile.json" +evidence = "reference/baseline.md" + +[projects.cases.expectation.comparison] +mode = "semantic-json" +tolerances = [] + +[[projects]] +id = "fao56-reference-evapotranspiration" +path = "projects/fao56-reference-evapotranspiration" +status = "active" +domain = "agricultural-water-management" +origin = "source-backed" +purpose = "Reproduce the FAO-56 Example 18 daily reference evapotranspiration result." + +[[projects.cases]] +id = "example-18" +entry = "src/fao56_reference_evapotranspiration/main.gcl" + +[projects.cases.expectation] +kind = "reference-backed" +evidence = "reference/calculation.md" + +[[projects.cases.expectation.assertions]] +pointer = "/node/reported_radiation_component/display_value" +expected = 2.81 + +[[projects.cases.expectation.assertions]] +pointer = "/node/reported_aerodynamic_component/display_value" +expected = 1.07 + +[[projects.cases.expectation.assertions]] +pointer = "/node/reported_equation_reference_evapotranspiration/display_value" +expected = 3.88 + +[[projects.cases.expectation.assertions]] +pointer = "/node/reported_reference_evapotranspiration/display_value" +expected = 3.9 +absolute_tolerance = 1e-12 + +[[projects.cases.expectation.assertions]] +pointer = "/node/reported_reference_evapotranspiration/unit" +expected = "mm_per_day" + +[[projects]] +id = "nws-heat-index-examples" +path = "projects/nws-heat-index-examples" +status = "active" +domain = "weather-safety" +origin = "source-backed" +purpose = "Reproduce two heat-index values explicitly reported by the National Weather Service." + +[[projects.cases]] +id = "published-examples" +entry = "src/nws_heat_index_examples/main.gcl" + +[projects.cases.expectation] +kind = "reference-backed" +evidence = "reference/calculation.md" + +[[projects.cases.expectation.assertions]] +pointer = "/node/humid_example_heat_index_fahrenheit_value/si_value" +expected = 124.0 + +[[projects.cases.expectation.assertions]] +pointer = "/node/dry_example_heat_index_fahrenheit_value/si_value" +expected = 96.0 + +[[projects]] +id = "epa-chp-savings-example" +path = "projects/epa-chp-savings-example" +status = "active" +domain = "energy-and-emissions" +origin = "source-backed" +purpose = "Reproduce fuel and carbon-dioxide savings reported in a U.S. EPA CHP example." + +[[projects.cases]] +id = "appendix-a" +entry = "src/epa_chp_savings_example/main.gcl" + +[projects.cases.expectation] +kind = "reference-backed" +evidence = "reference/calculation.md" + +[[projects.cases.expectation.assertions]] +pointer = "/node/total_fuel_savings/display_value" +expected = 115546.0 +absolute_tolerance = 1e-6 + +[[projects.cases.expectation.assertions]] +pointer = "/node/total_fuel_savings/unit" +expected = "MMBtu" + +[[projects.cases.expectation.assertions]] +pointer = "/node/reported_fuel_savings_percent/si_value" +expected = 21.0 + +[[projects.cases.expectation.assertions]] +pointer = "/node/total_co2_savings/display_value" +expected = 18065.0 +absolute_tolerance = 1e-9 + +[[projects.cases.expectation.assertions]] +pointer = "/node/total_co2_savings/unit" +expected = "short_ton" + +[[projects]] +id = "nws-wind-chill-example" +path = "projects/nws-wind-chill-example" +status = "active" +domain = "weather-safety" +origin = "source-backed" +purpose = "Reproduce a wind-chill value explicitly reported by the National Weather Service." + +[[projects.cases]] +id = "zero-fifteen" +entry = "src/nws_wind_chill_example/main.gcl" + +[projects.cases.expectation] +kind = "reference-backed" +evidence = "reference/calculation.md" + +[[projects.cases.expectation.assertions]] +pointer = "/node/reported_wind_chill_fahrenheit_value/si_value" +expected = -19.0 + +[[projects]] +id = "osha-noise-examples" +path = "projects/osha-noise-examples" +status = "active" +domain = "occupational-safety" +origin = "source-backed" +purpose = "Reproduce free-field sound-level and noise-dose results reported by OSHA." + +[[projects.cases]] +id = "manual-examples" +entry = "src/osha_noise_examples/main.gcl" + +[projects.cases.expectation] +kind = "reference-backed" +evidence = "reference/calculation.md" + +[[projects.cases.expectation.assertions]] +pointer = "/node/reported_target_sound_level_dba_value/si_value" +expected = 116.0 + +[[projects.cases.expectation.assertions]] +pointer = "/node/reported_eight_hour_twa_dba_value/si_value" +expected = 94.4 +absolute_tolerance = 1e-12 + +[[projects]] +id = "epa-cutback-asphalt-voc-example" +path = "projects/epa-cutback-asphalt-voc-example" +status = "active" +domain = "air-emissions" +origin = "source-backed" +purpose = "Reproduce diluent and VOC values reported in an EPA cutback-asphalt example." + +[[projects.cases]] +id = "ap-42-example" +entry = "src/epa_cutback_asphalt_voc_example/main.gcl" + +[projects.cases.expectation] +kind = "reference-backed" +evidence = "reference/calculation.md" + +[[projects.cases.expectation.assertions]] +pointer = "/node/reported_diluent_volume/display_value" +expected = 4900.0 +absolute_tolerance = 1e-9 + +[[projects.cases.expectation.assertions]] +pointer = "/node/reported_diluent_volume/unit" +expected = "L" + +[[projects.cases.expectation.assertions]] +pointer = "/node/reported_diluent_mass/display_value" +expected = 3400.0 +absolute_tolerance = 1e-9 + +[[projects.cases.expectation.assertions]] +pointer = "/node/reported_diluent_mass/unit" +expected = "kg" + +[[projects.cases.expectation.assertions]] +pointer = "/node/reported_voc_mass/display_value" +expected = 3200.0 +absolute_tolerance = 1e-9 + +[[projects.cases.expectation.assertions]] +pointer = "/node/reported_voc_mass/unit" +expected = "kg" + +[[projects.cases.expectation.assertions]] +pointer = "/node/reported_voc_percent_by_mass/si_value" +expected = 32.0 diff --git a/projects/epa-chp-savings-example/README.md b/projects/epa-chp-savings-example/README.md new file mode 100644 index 0000000..47051b4 --- /dev/null +++ b/projects/epa-chp-savings-example/README.md @@ -0,0 +1,5 @@ +# EPA combined-heat-and-power savings example + +This source-backed reproduction asks: **Do the U.S. EPA's published combined-heat-and-power fuel and carbon-dioxide savings equations reproduce the results in its CEESC worked example?** + +The project uses the rounded annual fuel-use and CO₂ values printed in Appendix A of the EPA methodology and evaluates the report's total-savings equations. The manifest checks only values explicitly reported by EPA. Source locations, unit adaptations, and assertion mappings are documented in [`reference/calculation.md`](reference/calculation.md). diff --git a/projects/epa-chp-savings-example/graphcal.toml b/projects/epa-chp-savings-example/graphcal.toml new file mode 100644 index 0000000..ac34c11 --- /dev/null +++ b/projects/epa-chp-savings-example/graphcal.toml @@ -0,0 +1,2 @@ +[package] +name = "epa_chp_savings_example" diff --git a/projects/epa-chp-savings-example/reference/calculation.md b/projects/epa-chp-savings-example/reference/calculation.md new file mode 100644 index 0000000..7d8dde9 --- /dev/null +++ b/projects/epa-chp-savings-example/reference/calculation.md @@ -0,0 +1,46 @@ +# EPA CHP savings source evidence + +## Candidate generation + +This source-backed candidate was prepared by an AI coding agent at the repository maintainer's direction from the cited public U.S. EPA worked example. It does not derive from private material. Human source, privacy, adaptation, and promotion review remain pull-request responsibilities. + +## Source and published expected values + +U.S. Environmental Protection Agency Combined Heat and Power Partnership, *Fuel and Carbon Dioxide Emissions Savings Calculation Methodology for Combined Heat and Power Systems*, June 2021, Appendix A, especially pages 20–21, https://www.epa.gov/sites/default/files/2015-07/documents/fuel_and_carbon_dioxide_emissions_savings_calculation_methodology_for_combined_heat_and_power_systems.pdf, accessed 2026-08-16. + +Appendix A publishes a complete CEESC example. Its Equation 1 reports total fuel savings as + +\[ +115{,}546=(257{,}964+300{,}437)-442{,}855\ \text{MMBtu/year}. +\] + +Its Equation 2 reports total carbon-dioxide savings as + +\[ +18{,}065=(15{,}078+28{,}872)-25{,}885\ \text{tons CO}_2/\text{year}. +\] + +Table 1 on page 20 also reports fuel savings of 115,546 MMBtu/year and 21%. + +These EPA-reported values—not locally generated higher-precision outputs—are the manifest's expected values. + +## Adaptation and source-to-assertion mapping + +The source quantities are annual totals. The Graphcal project represents the one-year fuel totals as `Energy` and CO₂ totals as `Mass`; it does not divide both sides by a year because that common reporting interval cancels in the savings equations. + +`MMBtu` is represented using the international-table Btu conversion, and `short_ton` uses the U.S. short ton. Conversion provenance is recorded by NIST, *Guide to the SI*, Appendix B.8, https://www.nist.gov/pml/special-publication-811/nist-guide-si-appendix-b-conversion-factors/nist-guide-si-appendix-b8. The assertions target source-unit display values so SI conversion does not become the numerical oracle. + +The manifest maps the source values as follows: + +- `/node/total_fuel_savings/display_value` = `115546.0` MMBtu, from Appendix A Equation 1 and Table 1; +- `/node/reported_fuel_savings_percent/si_value` = `21.0`, from Appendix A Table 1; +- `/node/total_co2_savings/display_value` = `18065.0` short tons, from Appendix A Equation 2; +- exact unit-label assertions distinguish the source-unit presentations. + +Small absolute tolerances on the two display values cover only binary floating-point roundoff introduced by unit scaling. They do not widen the source's published whole-unit precision. + +The report notes that some detailed figures do not equate exactly because of rounding. This project intentionally reproduces the rounded Equation 1 and Equation 2 arithmetic rather than claiming unreported precision. + +## Licensing and adaptation + +Only the equations and the small set of numerical facts needed for the cited worked result are reimplemented. No EPA prose, figures, calculator software, or substantial tables are copied. The Graphcal source and this evidence record are original corpus content under the repository license; the EPA and NIST pages are cited for provenance and are not relicensed here. diff --git a/projects/epa-chp-savings-example/src/epa_chp_savings_example/main.gcl b/projects/epa-chp-savings-example/src/epa_chp_savings_example/main.gcl new file mode 100644 index 0000000..abf07aa --- /dev/null +++ b/projects/epa-chp-savings-example/src/epa_chp_savings_example/main.gcl @@ -0,0 +1,17 @@ +const unit Btu_IT: Energy = 1055.056 J; +const unit MMBtu: Energy = 1000000.0 Btu_IT; +const unit short_ton: Mass = 907.18474 kg; + +param displaced_thermal_fuel_use: Energy = 257964.0 MMBtu; +param displaced_grid_fuel_use: Energy = 300437.0 MMBtu; +param chp_fuel_use: Energy = 442855.0 MMBtu; +param displaced_thermal_co2: Mass = 15078.0 short_ton; +param displaced_grid_co2: Mass = 28872.0 short_ton; +param chp_co2: Mass = 25885.0 short_ton; + +node conventional_fuel_use: Energy = @displaced_thermal_fuel_use + @displaced_grid_fuel_use; +node total_fuel_savings: Energy = @conventional_fuel_use - @chp_fuel_use -> MMBtu; +node reported_fuel_savings_percent: Dimensionless = round( + @total_fuel_savings / @conventional_fuel_use * 100.0 +); +node total_co2_savings: Mass = @displaced_thermal_co2 + @displaced_grid_co2 - @chp_co2 -> short_ton; diff --git a/projects/epa-cutback-asphalt-voc-example/README.md b/projects/epa-cutback-asphalt-voc-example/README.md new file mode 100644 index 0000000..7ea62e5 --- /dev/null +++ b/projects/epa-cutback-asphalt-voc-example/README.md @@ -0,0 +1,5 @@ +# EPA cutback-asphalt VOC example + +This source-backed reproduction asks: **Does the U.S. EPA AP-42 cutback-asphalt calculation reproduce the diluent and volatile-organic-compound values reported in its worked example?** + +The source reports about 4,900 L of diluent, 3,400 kg of diluent, 3,200 kg of VOC emissions, and 32% VOC by cutback-asphalt mass. The project preserves the source's staged rounding. Source locations and assertion mappings are documented in [`reference/calculation.md`](reference/calculation.md). diff --git a/projects/epa-cutback-asphalt-voc-example/graphcal.toml b/projects/epa-cutback-asphalt-voc-example/graphcal.toml new file mode 100644 index 0000000..61f536a --- /dev/null +++ b/projects/epa-cutback-asphalt-voc-example/graphcal.toml @@ -0,0 +1,2 @@ +[package] +name = "epa_cutback_asphalt_voc_example" diff --git a/projects/epa-cutback-asphalt-voc-example/reference/calculation.md b/projects/epa-cutback-asphalt-voc-example/reference/calculation.md new file mode 100644 index 0000000..358c3b7 --- /dev/null +++ b/projects/epa-cutback-asphalt-voc-example/reference/calculation.md @@ -0,0 +1,50 @@ +# EPA cutback-asphalt VOC source evidence + +## Candidate generation + +This source-backed candidate was prepared by an AI coding agent at the repository maintainer's direction from the cited public U.S. Environmental Protection Agency worked example. It does not derive from private material. Human source, privacy, adaptation, and promotion review remain pull-request responsibilities. + +## Source and published expected values + +U.S. Environmental Protection Agency, AP-42, Volume I, Chapter 4.5, “Asphalt Paving Operations,” Section 4.5.2, worked example on page 4.5-2, July 1979, reformatted January 1995, https://www.epa.gov/sites/default/files/2020-10/documents/c4s05.pdf, accessed 2026-08-16. + +The example uses: + +- 10,000 kg of rapid-cure cutback asphalt; +- 45% diluent by volume; +- diluent density of 0.7 kg/L; +- asphalt-cement density of 1.1 kg/L; and +- 95% of the diluent mass treated as evaporative VOC. + +The source solves the total-mass and volume-fraction equations and reports approximately: + +- 4,900 L of diluent; +- 3,400 kg of diluent; +- 3,200 kg of emitted VOC; and +- 32% of the cutback-asphalt mass emitted as VOC. + +Those four EPA-reported values—not locally generated higher-precision results—are the manifest's expected values. + +## Adaptation and source-to-assertion mapping + +Writing diluent volume as \(x\), asphalt-cement volume as \(y\), total mass as \(M\), diluent volume fraction as \(f\), and the two densities as \(\rho_d\) and \(\rho_a\), the source equations are + +\[ +M=\rho_dx+\rho_ay, \qquad x=f(x+y). +\] + +The model eliminates \(y\) to evaluate the same system. EPA reports staged approximate values: it first rounds diluent volume and mass, then multiplies the reported 3,400 kg by 0.95 and reports 3,200 kg. The Graphcal reporting nodes intentionally preserve that sequence instead of asserting local extra precision. + +The manifest maps the source outputs as: + +- `/node/reported_diluent_volume/display_value` = `4900.0` L; +- `/node/reported_diluent_mass/display_value` = `3400.0` kg; +- `/node/reported_voc_mass/display_value` = `3200.0` kg; +- `/node/reported_voc_percent_by_mass/si_value` = `32.0`; +- exact unit assertions require `L` and `kg` where applicable. + +Narrow display-value tolerances cover only binary unit-scaling representation and do not widen the source's hundred-unit precision. + +## Licensing and adaptation + +Only the equations and small set of numerical facts needed for the cited worked result are reimplemented. No EPA prose, figure, software, or substantial table is copied. The Graphcal source and this evidence record are original corpus content under the repository license; the EPA publication is cited for provenance and is not relicensed here. diff --git a/projects/epa-cutback-asphalt-voc-example/src/epa_cutback_asphalt_voc_example/main.gcl b/projects/epa-cutback-asphalt-voc-example/src/epa_cutback_asphalt_voc_example/main.gcl new file mode 100644 index 0000000..958c6d3 --- /dev/null +++ b/projects/epa-cutback-asphalt-voc-example/src/epa_cutback_asphalt_voc_example/main.gcl @@ -0,0 +1,30 @@ +const unit L: Volume = 0.001 m^3; + +param cutback_asphalt_mass: Mass = 10000.0 kg; +param diluent_volume_fraction: Dimensionless = 0.45; +param diluent_density: Mass / Volume = 0.7 kg/L; +param asphalt_cement_density: Mass / Volume = 1.1 kg/L; +param evaporated_diluent_fraction: Dimensionless = 0.95; + +const node volume_reporting_increment: Volume = 100.0 L; +const node mass_reporting_increment: Mass = 100.0 kg; + +node asphalt_to_diluent_volume_ratio: Dimensionless = ( + 1.0 - @diluent_volume_fraction +) / @diluent_volume_fraction; +node diluent_volume: Volume = @cutback_asphalt_mass / ( + @diluent_density + @asphalt_cement_density * @asphalt_to_diluent_volume_ratio +); +node diluent_mass: Mass = @diluent_volume * @diluent_density; +node reported_diluent_volume: Volume = round( + @diluent_volume / @volume_reporting_increment +) * @volume_reporting_increment -> L; +node reported_diluent_mass: Mass = round( + @diluent_mass / @mass_reporting_increment +) * @mass_reporting_increment -> kg; +node reported_voc_mass: Mass = round( + @reported_diluent_mass * @evaporated_diluent_fraction / @mass_reporting_increment +) * @mass_reporting_increment -> kg; +node reported_voc_percent_by_mass: Dimensionless = round( + @reported_voc_mass / @cutback_asphalt_mass * 100.0 +); diff --git a/projects/fao56-reference-evapotranspiration/README.md b/projects/fao56-reference-evapotranspiration/README.md new file mode 100644 index 0000000..17e195c --- /dev/null +++ b/projects/fao56-reference-evapotranspiration/README.md @@ -0,0 +1,5 @@ +# FAO-56 daily reference evapotranspiration + +This source-backed reproduction asks: **Does the FAO Penman–Monteith equation reproduce the daily reference evapotranspiration reported in FAO-56 Example 18?** + +It evaluates Equation 6 from the rounded intermediate meteorological values published for the example and exposes results at the precision printed by FAO. Celsius is handled explicitly as a normalized numeric value while physical temperatures remain kelvin quantities. The source-to-assertion mapping and unit adaptation are documented in [`reference/calculation.md`](reference/calculation.md). diff --git a/projects/fao56-reference-evapotranspiration/graphcal.toml b/projects/fao56-reference-evapotranspiration/graphcal.toml new file mode 100644 index 0000000..78f39c5 --- /dev/null +++ b/projects/fao56-reference-evapotranspiration/graphcal.toml @@ -0,0 +1,2 @@ +[package] +name = "fao56_reference_evapotranspiration" diff --git a/projects/fao56-reference-evapotranspiration/reference/calculation.md b/projects/fao56-reference-evapotranspiration/reference/calculation.md new file mode 100644 index 0000000..a3131da --- /dev/null +++ b/projects/fao56-reference-evapotranspiration/reference/calculation.md @@ -0,0 +1,60 @@ +# FAO-56 reference-evapotranspiration source evidence + +## Candidate generation + +This source-backed candidate was prepared by an AI coding agent at the repository maintainer's direction from the cited public worked example. It does not derive from private material. Human source, privacy, adaptation, and promotion review remain pull-request responsibilities. + +## Source and published expected values + +R. G. Allen, L. S. Pereira, D. Raes, and M. Smith, *Crop Evapotranspiration: Guidelines for Computing Crop Water Requirements*, FAO Irrigation and Drainage Paper 56, Food and Agriculture Organization of the United Nations, Rome, 1998, Chapter 4, Equation 6 and Example 18, https://www.fao.org/4/x0490e/x0490e08.htm, accessed 2026-08-15. + +For a daily time step, the source gives the FAO Penman–Monteith equation + +\[ +ET_0 = +\frac{ +0.408\,\Delta(R_n-G) ++ \gamma\frac{900}{T+273}u_2(e_s-e_a) +}{ +\Delta+\gamma(1+0.34u_2) +}. +\] + +Example 18 publishes the rounded intermediate inputs reproduced by the project: + +- \(T=16.9\ ^\circ\mathrm{C}\); +- \(u_2=2.078\ \mathrm{m/s}\); +- \(\Delta=0.122\ \mathrm{kPa/^{\circ}C}\); +- \(\gamma=0.0666\ \mathrm{kPa/^{\circ}C}\); +- \(e_s-e_a=0.589\ \mathrm{kPa}\); +- \(R_n=13.28\ \mathrm{MJ/(m^2\,day)}\); +- \(G=0\ \mathrm{MJ/(m^2\,day)}\). + +The worked example then reports: + +- radiation component: 2.81 mm/day; +- aerodynamic component: 1.07 mm/day; +- Equation 6 total before final presentation: 3.88 mm/day; +- final reported reference evapotranspiration: 3.9 mm/day. + +Those four source-reported values—not locally calculated higher-precision values—are the manifest's expected values. + +## Adaptation and source-to-assertion mapping + +Graphcal intentionally has no affine Celsius unit. The project stores 16.9 °C as 290.05 K and explicitly obtains the conventional Celsius numeric value as \((T_K-273.15\,K)/(1\,K)\) before using the empirical \(T+273\) term. One degree Celsius and one kelvin have the same interval size, so \(\Delta\) and \(\gamma\) use kPa/K. + +The empirical equation requires values normalized to its prescribed conventional units. The model makes those normalizations explicit, attaches mm/day to the output, and adds reporting nodes at the precision printed by Example 18. + +The manifest maps the published outputs as follows: + +- `/node/reported_radiation_component/display_value` = `2.81`; +- `/node/reported_aerodynamic_component/display_value` = `1.07`; +- `/node/reported_equation_reference_evapotranspiration/display_value` = `3.88`; +- `/node/reported_reference_evapotranspiration/display_value` = `3.9`; +- exact unit assertions require `mm_per_day`. + +The unrounded internal nodes remain useful corpus coverage, but they are deliberately not reference-backed assertions because FAO does not report those extra digits. + +## Licensing and adaptation + +Only the equation and the small set of numerical facts needed for the cited worked result are reimplemented. No FAO prose, figures, calculation-sheet layout, software, or data files are copied. The Graphcal source and this evidence record are original corpus content under the repository license; the FAO publication is cited for provenance and is not relicensed here. diff --git a/projects/fao56-reference-evapotranspiration/src/fao56_reference_evapotranspiration/main.gcl b/projects/fao56-reference-evapotranspiration/src/fao56_reference_evapotranspiration/main.gcl new file mode 100644 index 0000000..1aa410e --- /dev/null +++ b/projects/fao56-reference-evapotranspiration/src/fao56_reference_evapotranspiration/main.gcl @@ -0,0 +1,46 @@ +const unit day: Time = 24.0 h; +const unit MJ: Energy = 1000.0 kJ; +const unit mm_per_day: Velocity = 1.0 mm/day; +const unit MJ_per_square_metre_day: Power / Area = 1.0 MJ/m^2/day; +const unit kPa_per_K: Pressure / Temperature = 1.0 kPa/K; + +param mean_air_temperature: Temperature = 290.05 K; +param wind_speed_2m: Velocity = 2.078 m/s; +param slope_vapour_pressure_curve: Pressure / Temperature = 0.122 kPa_per_K; +param psychrometric_constant: Pressure / Temperature = 0.0666 kPa_per_K; +param vapour_pressure_deficit: Pressure = 0.589 kPa; +param net_radiation_flux: Power / Area = 13.28 MJ_per_square_metre_day; +param soil_heat_flux: Power / Area = 0.0 MJ_per_square_metre_day; + +const node equation_reporting_increment: Velocity = 0.01 mm_per_day; +const node final_reporting_increment: Velocity = 0.1 mm_per_day; + +node mean_air_temperature_celsius_value: Dimensionless = (@mean_air_temperature - 273.15 K) / 1.0 K; +node normalized_wind_speed: Dimensionless = @wind_speed_2m / 1.0 m/s; +node normalized_slope: Dimensionless = @slope_vapour_pressure_curve / 1.0 kPa_per_K; +node normalized_psychrometric_constant: Dimensionless = @psychrometric_constant / 1.0 kPa_per_K; +node normalized_vapour_pressure_deficit: Dimensionless = @vapour_pressure_deficit / 1.0 kPa; +node normalized_available_radiation: Dimensionless = ( + @net_radiation_flux - @soil_heat_flux +) / 1.0 MJ_per_square_metre_day; +node denominator: Dimensionless = @normalized_slope + @normalized_psychrometric_constant * ( + 1.0 + 0.34 * @normalized_wind_speed +); + +node radiation_component: Velocity = 0.408 * @normalized_available_radiation * @normalized_slope / @denominator * 1.0 mm_per_day -> mm_per_day; +node aerodynamic_component: Velocity = @normalized_psychrometric_constant * ( + 900.0 / (@mean_air_temperature_celsius_value + 273.0) +) * @normalized_wind_speed * @normalized_vapour_pressure_deficit / @denominator * 1.0 mm_per_day -> mm_per_day; +node reference_evapotranspiration: Velocity = @radiation_component + @aerodynamic_component -> mm_per_day; +node reported_radiation_component: Velocity = round( + @radiation_component / @equation_reporting_increment +) * @equation_reporting_increment -> mm_per_day; +node reported_aerodynamic_component: Velocity = round( + @aerodynamic_component / @equation_reporting_increment +) * @equation_reporting_increment -> mm_per_day; +node reported_equation_reference_evapotranspiration: Velocity = round( + @reference_evapotranspiration / @equation_reporting_increment +) * @equation_reporting_increment -> mm_per_day; +node reported_reference_evapotranspiration: Velocity = round( + @reference_evapotranspiration / @final_reporting_increment +) * @final_reporting_increment -> mm_per_day; diff --git a/projects/nasa-lift-sweep/README.md b/projects/nasa-lift-sweep/README.md new file mode 100644 index 0000000..2428731 --- /dev/null +++ b/projects/nasa-lift-sweep/README.md @@ -0,0 +1,5 @@ +# NASA lift-equation sweep + +This source-informed synthetic project asks: **What dynamic pressure and lift result at three low-speed flight points, and what speed produces a required lift?** + +It applies NASA's lift and dynamic-pressure equations to original illustrative inputs. NASA does not publish expected values for this scenario, so the checked-in output is a stability baseline rather than an aerodynamic correctness claim. Its generation and determinism evidence are documented in [`reference/baseline.md`](reference/baseline.md). diff --git a/projects/nasa-lift-sweep/expected/three-flight-points.json b/projects/nasa-lift-sweep/expected/three-flight-points.json new file mode 100644 index 0000000..ace92f1 --- /dev/null +++ b/projects/nasa-lift-sweep/expected/three-flight-points.json @@ -0,0 +1,98 @@ +{ + "const": {}, + "node": { + "cruise_dynamic_pressure": { + "display_value": 980.0000000000001, + "si_value": 980.0000000000001, + "unit": "Pa" + }, + "cruise_lift": { + "display_value": 12.700800000000001, + "si_value": 12700.800000000001, + "unit": "kN" + }, + "dynamic_pressure": { + "entries": { + "Approach": { + "si_value": 551.25, + "unit": "kg/m/s^2" + }, + "Cruise": { + "si_value": 980.0000000000001, + "unit": "kg/m/s^2" + }, + "Fast": { + "si_value": 1531.25, + "unit": "kg/m/s^2" + } + }, + "index": "FlightPoint" + }, + "level_flight_speed": { + "si_value": 38.88078956798696, + "unit": "m/s" + }, + "lift_force": { + "entries": { + "Approach": { + "si_value": 7144.2, + "unit": "m*kg/s^2" + }, + "Cruise": { + "si_value": 12700.800000000001, + "unit": "m*kg/s^2" + }, + "Fast": { + "si_value": 19845.0, + "unit": "m*kg/s^2" + } + }, + "index": "FlightPoint" + }, + "maximum_lift": { + "display_value": 19.845, + "si_value": 19845.0, + "unit": "kN" + } + }, + "param": { + "air_density": { + "display_value": 1.225, + "si_value": 1.225, + "unit": "kg/m^3" + }, + "airspeed": { + "entries": { + "Approach": { + "display_value": 30.0, + "si_value": 30.0, + "unit": "m/s" + }, + "Cruise": { + "display_value": 40.0, + "si_value": 40.0, + "unit": "m/s" + }, + "Fast": { + "display_value": 50.0, + "si_value": 50.0, + "unit": "m/s" + } + }, + "index": "FlightPoint" + }, + "lift_coefficient": { + "si_value": 0.8 + }, + "required_lift": { + "display_value": 12.0, + "si_value": 12000.0, + "unit": "kN" + }, + "wing_area": { + "display_value": 16.2, + "si_value": 16.2, + "unit": "m^2" + } + } +} diff --git a/projects/nasa-lift-sweep/graphcal.toml b/projects/nasa-lift-sweep/graphcal.toml new file mode 100644 index 0000000..a1286fe --- /dev/null +++ b/projects/nasa-lift-sweep/graphcal.toml @@ -0,0 +1,2 @@ +[package] +name = "nasa_lift_sweep" diff --git a/projects/nasa-lift-sweep/reference/baseline.md b/projects/nasa-lift-sweep/reference/baseline.md new file mode 100644 index 0000000..7bd871f --- /dev/null +++ b/projects/nasa-lift-sweep/reference/baseline.md @@ -0,0 +1,43 @@ +# Stability baseline evidence + +## Classification and generation + +This is an original synthetic, source-informed project prepared as a candidate by an AI coding agent at the repository maintainer's direction. NASA publishes the lift and dynamic-pressure equations, but it does not publish expected outputs for this project's invented density, wing, coefficient, speeds, and required lift. The checked-in output is therefore a Graphcal stability baseline, not an aerodynamic correctness oracle. The project does not derive from private material. Human privacy, technical, and promotion review remain pull-request responsibilities. + +## Producing revisions and inputs + +- Graphcal: `0.0.1-alpha.23`, commit `04ab7150534de0b795acae1b3a7bcc282266ec3c` +- Corpus source revision: `42259833d1b8d6c60c761195a4a7981019058e17` +- Entrypoint: `src/nasa_lift_sweep/main.gcl` +- Inputs: checked-in parameter defaults; no `--set` or `--input` overrides +- Expected output: `expected/three-flight-points.json` +- Canonical expected-output SHA-256: `c06e90a3f2e52cd3a621bb2a2c49e7beea35a5413059288105d25dbe354bf299` + +The source equations are documented by NASA Glenn Research Center, “Lift Coefficient,” *Beginner's Guide to Aeronautics*, https://www1.grc.nasa.gov/beginners-guide-to-aeronautics/lift-coefficient/, accessed 2026-08-15. That citation motivates the model but does not supply its expected values. + +The baseline was generated from the project directory with: + +```console +graphcal eval --format json --root . src/nasa_lift_sweep/main.gcl \ + | jq -S . > expected/three-flight-points.json +``` + +## Comparison rules + +The runner parses both outputs as strict JSON and compares them semantically. Object key order is ignored; array order and length are significant; missing or additional values fail; and JSON values compare exactly by type and value. This baseline declares no numeric tolerances. + +Exact comparison intentionally detects deterministic changes across indexed evaluation, powers, reductions, square roots, and unit display. It does not prove that the synthetic aerodynamic assumptions are correct. + +## Repeated-run determinism + +Five consecutive evaluations with the recorded Graphcal executable and source revision produced the same canonical semantic JSON. Each result was canonicalized with `jq -S -c` and hashed with SHA-256: + +```text +c06e90a3f2e52cd3a621bb2a2c49e7beea35a5413059288105d25dbe354bf299 +c06e90a3f2e52cd3a621bb2a2c49e7beea35a5413059288105d25dbe354bf299 +c06e90a3f2e52cd3a621bb2a2c49e7beea35a5413059288105d25dbe354bf299 +c06e90a3f2e52cd3a621bb2a2c49e7beea35a5413059288105d25dbe354bf299 +c06e90a3f2e52cd3a621bb2a2c49e7beea35a5413059288105d25dbe354bf299 +``` + +This establishes repeatability only for the recorded executable and inputs. diff --git a/projects/nasa-lift-sweep/src/nasa_lift_sweep/main.gcl b/projects/nasa-lift-sweep/src/nasa_lift_sweep/main.gcl new file mode 100644 index 0000000..fa4861d --- /dev/null +++ b/projects/nasa-lift-sweep/src/nasa_lift_sweep/main.gcl @@ -0,0 +1,24 @@ +pub index FlightPoint = { Approach, Cruise, Fast }; + +param air_density: Mass / Volume = 1.225 kg/m^3; +param wing_area: Area = 16.2 m^2; +param lift_coefficient: Dimensionless = 0.8; +param airspeed: Velocity[FlightPoint] = { + FlightPoint.Approach: 30.0 m/s, + FlightPoint.Cruise: 40.0 m/s, + FlightPoint.Fast: 50.0 m/s, +}; +param required_lift: Force = 12.0 kN; + +node dynamic_pressure: Pressure[FlightPoint] = for point: FlightPoint { + 0.5 * @air_density * @airspeed[point] ^ 2 +}; +node lift_force: Force[FlightPoint] = for point: FlightPoint { + @lift_coefficient * @dynamic_pressure[point] * @wing_area +}; +node cruise_dynamic_pressure: Pressure = @dynamic_pressure[FlightPoint.Cruise] -> Pa; +node cruise_lift: Force = @lift_force[FlightPoint.Cruise] -> kN; +node maximum_lift: Force = maximum(@lift_force) -> kN; +node level_flight_speed: Velocity = sqrt( + 2.0 * @required_lift / (@air_density * @wing_area * @lift_coefficient) +); diff --git a/projects/nws-heat-index-examples/README.md b/projects/nws-heat-index-examples/README.md new file mode 100644 index 0000000..a401eef --- /dev/null +++ b/projects/nws-heat-index-examples/README.md @@ -0,0 +1,5 @@ +# NWS heat-index examples + +This source-backed reproduction asks: **Does the National Weather Service heat-index regression reproduce two values explicitly reported by NWS for 100 °F air?** + +The NWS page publishes the regression and reports heat indexes of 124 °F at 55% relative humidity and 96 °F at 15% relative humidity. The project applies the published regression and rounds to the whole-degree presentation used by the source. The source-to-assertion mapping and the required Fahrenheit adaptation are documented in [`reference/calculation.md`](reference/calculation.md). diff --git a/projects/nws-heat-index-examples/graphcal.toml b/projects/nws-heat-index-examples/graphcal.toml new file mode 100644 index 0000000..6201658 --- /dev/null +++ b/projects/nws-heat-index-examples/graphcal.toml @@ -0,0 +1,2 @@ +[package] +name = "nws_heat_index_examples" diff --git a/projects/nws-heat-index-examples/reference/calculation.md b/projects/nws-heat-index-examples/reference/calculation.md new file mode 100644 index 0000000..2c2525a --- /dev/null +++ b/projects/nws-heat-index-examples/reference/calculation.md @@ -0,0 +1,45 @@ +# NWS heat-index source evidence + +## Candidate generation + +This source-backed candidate was prepared by an AI coding agent at the repository maintainer's direction from the cited public National Weather Service page. It does not derive from private material. Human source, privacy, adaptation, and promotion review remain pull-request responsibilities. + +## Source and published expected values + +National Weather Service, Weather Forecast Office Amarillo, “What is the heat index?”, https://www.weather.gov/ama/heatindex, accessed 2026-08-16. + +The page publishes the Rothfusz regression + +\[ +\begin{aligned} +HI={}&-42.379+2.04901523T+10.14333127RH-0.22475541T RH\\ +&-0.00683783T^2-0.05481717RH^2+0.00122874T^2RH\\ +&+0.00085282T RH^2-0.00000199T^2RH^2, +\end{aligned} +\] + +with \(T\) in degrees Fahrenheit, \(RH\) as a percentage, and \(HI\) in degrees Fahrenheit. The same NWS page explicitly reports these examples: + +| Example | Source air temperature | Source relative humidity | Source heat index | +|---|---:|---:|---:| +| Humid | 100 °F | 55% | 124 °F | +| Dry | 100 °F | 15% | 96 °F | + +Those two source-reported output values—not a locally calculated higher-precision result—are the manifest's expected values. + +## Adaptation and source-to-assertion mapping + +Graphcal does not define affine Fahrenheit units. The regression itself requires conventional Fahrenheit and percentage numeric values, so the project names and types those inputs explicitly as dimensionless numeric values rather than pretending they are multiplicative temperature quantities. The output is likewise named `*_fahrenheit_value`. + +The page presents the examples to whole degrees. Evaluating the published regression gives values near 123.64 and 95.85 before presentation; Graphcal applies `round` and the manifest asserts exactly: + +- `/node/humid_example_heat_index_fahrenheit_value/si_value` = `124.0`, from the NWS 100 °F / 55% example; +- `/node/dry_example_heat_index_fahrenheit_value/si_value` = `96.0`, from the NWS 100 °F / 15% example. + +The local regression evaluation only connects the model to the published whole-degree examples; it is not the source of the expected values. + +The NWS notes that the regression is an approximation with an error of ±1.3 °F and that chart values are for shady locations. This project reproduces the two reported examples only; it does not make a broader heat-safety claim. + +## Licensing and adaptation + +Only the published equation and two numerical examples are reimplemented. No source prose, chart image, software, or dataset is copied. The Graphcal source and this evidence record are original corpus content under the repository license; the NWS page is cited for provenance and is not relicensed here. diff --git a/projects/nws-heat-index-examples/src/nws_heat_index_examples/main.gcl b/projects/nws-heat-index-examples/src/nws_heat_index_examples/main.gcl new file mode 100644 index 0000000..3bae37c --- /dev/null +++ b/projects/nws-heat-index-examples/src/nws_heat_index_examples/main.gcl @@ -0,0 +1,19 @@ +pub index Example = { Humid, Dry }; + +param air_temperature_fahrenheit_value: Dimensionless[Example] = { + Example.Humid: 100.0, + Example.Dry: 100.0, +}; +param relative_humidity_percent_value: Dimensionless[Example] = { + Example.Humid: 55.0, + Example.Dry: 15.0, +}; + +node regression_heat_index_fahrenheit_value: Dimensionless[Example] = for example: Example { + -42.379 + 2.04901523 * @air_temperature_fahrenheit_value[example] + 10.14333127 * @relative_humidity_percent_value[example] - 0.22475541 * @air_temperature_fahrenheit_value[example] * @relative_humidity_percent_value[example] - 0.00683783 * @air_temperature_fahrenheit_value[example] ^ 2 - 0.05481717 * @relative_humidity_percent_value[example] ^ 2 + 0.00122874 * @air_temperature_fahrenheit_value[example] ^ 2 * @relative_humidity_percent_value[example] + 0.00085282 * @air_temperature_fahrenheit_value[example] * @relative_humidity_percent_value[example] ^ 2 - 0.00000199 * @air_temperature_fahrenheit_value[example] ^ 2 * @relative_humidity_percent_value[example] ^ 2 +}; +node reported_heat_index_fahrenheit_value: Dimensionless[Example] = for example: Example { + round(@regression_heat_index_fahrenheit_value[example]) +}; +node humid_example_heat_index_fahrenheit_value: Dimensionless = @reported_heat_index_fahrenheit_value[Example.Humid]; +node dry_example_heat_index_fahrenheit_value: Dimensionless = @reported_heat_index_fahrenheit_value[Example.Dry]; diff --git a/projects/nws-wind-chill-example/README.md b/projects/nws-wind-chill-example/README.md new file mode 100644 index 0000000..11b9bff --- /dev/null +++ b/projects/nws-wind-chill-example/README.md @@ -0,0 +1,5 @@ +# NWS wind-chill example + +This source-backed reproduction asks: **Does the National Weather Service wind-chill equation reproduce the NWS example for 0 °F air and a 15 mph wind?** + +The NWS publishes both the equation and the expected whole-degree result of −19 °F. The source-to-assertion mapping and Fahrenheit adaptation are documented in [`reference/calculation.md`](reference/calculation.md). diff --git a/projects/nws-wind-chill-example/graphcal.toml b/projects/nws-wind-chill-example/graphcal.toml new file mode 100644 index 0000000..42b3b83 --- /dev/null +++ b/projects/nws-wind-chill-example/graphcal.toml @@ -0,0 +1,2 @@ +[package] +name = "nws_wind_chill_example" diff --git a/projects/nws-wind-chill-example/reference/calculation.md b/projects/nws-wind-chill-example/reference/calculation.md new file mode 100644 index 0000000..12c6ac1 --- /dev/null +++ b/projects/nws-wind-chill-example/reference/calculation.md @@ -0,0 +1,35 @@ +# NWS wind-chill source evidence + +## Candidate generation + +This source-backed candidate was prepared by an AI coding agent at the repository maintainer's direction from the cited public National Weather Service page. It does not derive from private material. Human source, privacy, adaptation, and promotion review remain pull-request responsibilities. + +## Source and published expected value + +National Weather Service, “Understanding Wind Chill,” https://www.weather.gov/safety/cold-wind-chill-chart, accessed 2026-08-16. + +The page publishes the U.S. wind-chill equation + +\[ +W = 35.74 + 0.6215T - 35.75V^{0.16} + 0.4275TV^{0.16}, +\] + +where \(T\) is air temperature in degrees Fahrenheit and \(V\) is wind speed in miles per hour. The same page explicitly states that 0 °F air with a 15 mph wind produces a wind chill of −19 °F. + +That source-reported −19 °F value—not a locally generated higher-precision result—is the manifest's expected value. + +## Adaptation and source-to-assertion mapping + +Graphcal has no affine Fahrenheit unit, so the model explicitly names the temperature and output as conventional Fahrenheit numeric values. Wind speed remains a physical `Velocity` and is normalized by an explicitly defined mph before entering the empirical equation. + +The source presents the result to a whole degree. The model applies `round`, and the manifest maps the source output as: + +- `/node/reported_wind_chill_fahrenheit_value/si_value` = `-19.0`. + +The unrounded internal result is intentionally not asserted because the NWS page does not report those extra digits. + +The NWS defines wind chill only for temperatures at or below 50 °F and wind speeds above 3 mph. The reproduced inputs are within that domain. This case reproduces one published example and does not make a broader weather-safety claim. + +## Licensing and adaptation + +Only the published equation and one numerical example are reimplemented. No source prose, chart image, calculator software, or dataset is copied. The Graphcal source and this evidence record are original corpus content under the repository license; the NWS page is cited for provenance and is not relicensed here. diff --git a/projects/nws-wind-chill-example/src/nws_wind_chill_example/main.gcl b/projects/nws-wind-chill-example/src/nws_wind_chill_example/main.gcl new file mode 100644 index 0000000..ca41937 --- /dev/null +++ b/projects/nws-wind-chill-example/src/nws_wind_chill_example/main.gcl @@ -0,0 +1,9 @@ +const unit mph: Velocity = 0.44704 m/s; + +param air_temperature_fahrenheit_value: Dimensionless = 0.0; +param wind_speed: Velocity = 15.0 mph; + +node wind_speed_mph_value: Dimensionless = @wind_speed / 1.0 mph; +node wind_speed_factor: Dimensionless = @wind_speed_mph_value ^ 0.16; +node wind_chill_fahrenheit_value: Dimensionless = 35.74 + 0.6215 * @air_temperature_fahrenheit_value - 35.75 * @wind_speed_factor + 0.4275 * @air_temperature_fahrenheit_value * @wind_speed_factor; +node reported_wind_chill_fahrenheit_value: Dimensionless = round(@wind_chill_fahrenheit_value); diff --git a/projects/osha-noise-examples/README.md b/projects/osha-noise-examples/README.md new file mode 100644 index 0000000..6713585 --- /dev/null +++ b/projects/osha-noise-examples/README.md @@ -0,0 +1,5 @@ +# OSHA noise-calculation examples + +This source-backed reproduction asks: **Do two equations in the OSHA Technical Manual reproduce OSHA's published free-field sound-level and noise-dose examples?** + +The source reports 116 dBA at 80 ft for its aircraft-engine example and 94.4 dBA for its 183% dose example. The model reports only the source precision. The source-to-assertion mappings and logarithmic-level conventions are documented in [`reference/calculation.md`](reference/calculation.md). diff --git a/projects/osha-noise-examples/graphcal.toml b/projects/osha-noise-examples/graphcal.toml new file mode 100644 index 0000000..5c5bace --- /dev/null +++ b/projects/osha-noise-examples/graphcal.toml @@ -0,0 +1,2 @@ +[package] +name = "osha_noise_examples" diff --git a/projects/osha-noise-examples/reference/calculation.md b/projects/osha-noise-examples/reference/calculation.md new file mode 100644 index 0000000..3b23f20 --- /dev/null +++ b/projects/osha-noise-examples/reference/calculation.md @@ -0,0 +1,44 @@ +# OSHA noise-example source evidence + +## Candidate generation + +This source-backed candidate was prepared by an AI coding agent at the repository maintainer's direction from the cited public Occupational Safety and Health Administration manual. It does not derive from private material. Human source, privacy, adaptation, and promotion review remain pull-request responsibilities. + +## Source and published expected values + +Occupational Safety and Health Administration, *OSHA Technical Manual*, Section III, Chapter 5, “Noise,” Appendix B.6 and B.8, https://www.osha.gov/otm/section-3-health-hazards/chapter-5, accessed 2026-08-16. + +Appendix B.6 publishes the free-field distance equation + +\[ +L_{p,d2}=L_{p,d1}+20\log_{10}(d_1/d_2). +\] + +Its worked aircraft-engine example gives \(L_{p,d1}=120\) dBA at \(d_1=50\) ft and asks for the level at \(d_2=80\) ft. OSHA reports the result as 116 dBA. + +Appendix B.8 publishes the eight-hour dose conversion + +\[ +TWA=16.61\log_{10}(D/100)+90. +\] + +Its worked example uses \(D=183\%\) and reports an eight-hour TWA of 94.4 dBA. + +Those source-reported 116 dBA and 94.4 dBA values—not locally generated higher-precision results—are the manifest's expected values. + +## Adaptation and source-to-assertion mapping + +A-weighted decibels are logarithmic levels, not multiplicative physical quantities, so the project names them explicitly as dimensionless dBA numeric values. Distances retain the physical `Length` type and cancel to the dimensionless ratio required by the logarithm. + +The model rounds each output to the precision printed by OSHA. The manifest maps the source outputs as: + +- `/node/reported_target_sound_level_dba_value/si_value` = `116.0`, from Appendix B.6; +- `/node/reported_eight_hour_twa_dba_value/si_value` = `94.4`, from Appendix B.8. + +A narrow tolerance on 94.4 covers only binary representation of the reported decimal; it does not assert unreported precision. + +The distance equation assumes a point source in a free field without walls or obstructions, exactly as stated for the source example. This project does not generalize the result to reflective workplaces or non-point sources. + +## Licensing and adaptation + +Only two published equations and their small worked examples are reimplemented. No source prose, figures, software, or substantial table is copied. The Graphcal source and this evidence record are original corpus content under the repository license; the OSHA page is cited for provenance and is not relicensed here. diff --git a/projects/osha-noise-examples/src/osha_noise_examples/main.gcl b/projects/osha-noise-examples/src/osha_noise_examples/main.gcl new file mode 100644 index 0000000..bb2c9e3 --- /dev/null +++ b/projects/osha-noise-examples/src/osha_noise_examples/main.gcl @@ -0,0 +1,19 @@ +const unit ft: Length = 0.3048 m; + +param initial_sound_level_dba_value: Dimensionless = 120.0; +param initial_distance: Length = 50.0 ft; +param target_distance: Length = 80.0 ft; +param noise_dose_percent_value: Dimensionless = 183.0; + +const node twa_reporting_increment: Dimensionless = 0.1; + +node target_sound_level_dba_value: Dimensionless = @initial_sound_level_dba_value + 20.0 * log10( + @initial_distance / @target_distance +); +node reported_target_sound_level_dba_value: Dimensionless = round(@target_sound_level_dba_value); +node eight_hour_twa_dba_value: Dimensionless = 16.61 * log10( + @noise_dose_percent_value / 100.0 +) + 90.0; +node reported_eight_hour_twa_dba_value: Dimensionless = round( + @eight_hour_twa_dba_value / @twa_reporting_increment +) * @twa_reporting_increment; diff --git a/projects/pvwatts-hourly-dc-energy/README.md b/projects/pvwatts-hourly-dc-energy/README.md new file mode 100644 index 0000000..f1c0a61 --- /dev/null +++ b/projects/pvwatts-hourly-dc-energy/README.md @@ -0,0 +1,5 @@ +# PVWatts hourly DC energy + +This source-informed synthetic project asks: **How much DC energy does a 5 kW photovoltaic array produce over four one-hour conditions when the PVWatts module model is applied?** + +It uses the published PVWatts Version 5 DC module equation with an original four-period profile. NREL does not publish expected values for these inputs, so the checked-in output is a stability baseline rather than a photovoltaic correctness claim. Its generation and determinism evidence are documented in [`reference/baseline.md`](reference/baseline.md). diff --git a/projects/pvwatts-hourly-dc-energy/expected/four-hour-profile.json b/projects/pvwatts-hourly-dc-energy/expected/four-hour-profile.json new file mode 100644 index 0000000..73ba25a --- /dev/null +++ b/projects/pvwatts-hourly-dc-energy/expected/four-hour-profile.json @@ -0,0 +1,156 @@ +{ + "const": { + "reference_cell_temperature": { + "display_value": 298.15, + "si_value": 298.15, + "unit": "K" + }, + "reference_irradiance": { + "display_value": 1000.0, + "si_value": 1000.0, + "unit": "W/m^2" + } + }, + "node": { + "dc_power": { + "entries": { + "Afternoon": { + "si_value": 1920.0, + "unit": "m^2*kg/s^3" + }, + "LowSun": { + "si_value": 1040.0, + "unit": "m^2*kg/s^3" + }, + "MidMorning": { + "si_value": 3000.0, + "unit": "m^2*kg/s^3" + }, + "SolarNoon": { + "si_value": 4600.0, + "unit": "m^2*kg/s^3" + } + }, + "index": "Period" + }, + "interval_dc_energy": { + "entries": { + "Afternoon": { + "si_value": 6912000.0, + "unit": "m^2*kg/s^2" + }, + "LowSun": { + "si_value": 3744000.0, + "unit": "m^2*kg/s^2" + }, + "MidMorning": { + "si_value": 10800000.0, + "unit": "m^2*kg/s^2" + }, + "SolarNoon": { + "si_value": 16560000.0, + "unit": "m^2*kg/s^2" + } + }, + "index": "Period" + }, + "low_sun_dc_power": { + "display_value": 1.04, + "si_value": 1040.0, + "unit": "kW" + }, + "peak_dc_power": { + "display_value": 4.6, + "si_value": 4600.0, + "unit": "kW" + }, + "temperature_factor": { + "entries": { + "Afternoon": { + "si_value": 0.96 + }, + "LowSun": { + "si_value": 1.04 + }, + "MidMorning": { + "si_value": 1.0 + }, + "SolarNoon": { + "si_value": 0.92 + } + }, + "index": "Period" + }, + "total_dc_energy": { + "display_value": 10.56, + "si_value": 38016000.0, + "unit": "kWh" + } + }, + "param": { + "cell_temperature": { + "entries": { + "Afternoon": { + "display_value": 308.15, + "si_value": 308.15, + "unit": "K" + }, + "LowSun": { + "display_value": 288.15, + "si_value": 288.15, + "unit": "K" + }, + "MidMorning": { + "display_value": 298.15, + "si_value": 298.15, + "unit": "K" + }, + "SolarNoon": { + "display_value": 318.15, + "si_value": 318.15, + "unit": "K" + } + }, + "index": "Period" + }, + "effective_irradiance": { + "entries": { + "Afternoon": { + "display_value": 400.0, + "si_value": 400.0, + "unit": "W/m^2" + }, + "LowSun": { + "display_value": 200.0, + "si_value": 200.0, + "unit": "W/m^2" + }, + "MidMorning": { + "display_value": 600.0, + "si_value": 600.0, + "unit": "W/m^2" + }, + "SolarNoon": { + "display_value": 1000.0, + "si_value": 1000.0, + "unit": "W/m^2" + } + }, + "index": "Period" + }, + "interval_duration": { + "display_value": 1.0, + "si_value": 3600.0, + "unit": "h" + }, + "nameplate_dc_power": { + "display_value": 5.0, + "si_value": 5000.0, + "unit": "kW" + }, + "temperature_coefficient": { + "si_value": -0.004, + "unit": "K^-1" + } + } +} diff --git a/projects/pvwatts-hourly-dc-energy/graphcal.toml b/projects/pvwatts-hourly-dc-energy/graphcal.toml new file mode 100644 index 0000000..5faa3bd --- /dev/null +++ b/projects/pvwatts-hourly-dc-energy/graphcal.toml @@ -0,0 +1,2 @@ +[package] +name = "pvwatts_hourly_dc_energy" diff --git a/projects/pvwatts-hourly-dc-energy/reference/baseline.md b/projects/pvwatts-hourly-dc-energy/reference/baseline.md new file mode 100644 index 0000000..2ca0c24 --- /dev/null +++ b/projects/pvwatts-hourly-dc-energy/reference/baseline.md @@ -0,0 +1,43 @@ +# Stability baseline evidence + +## Classification and generation + +This is an original synthetic, source-informed project prepared as a candidate by an AI coding agent at the repository maintainer's direction. NREL publishes the PVWatts Version 5 module equation, but it does not publish expected outputs for this project's invented four-period irradiance and temperature profile. The checked-in output is therefore a Graphcal stability baseline, not a photovoltaic correctness oracle. The project does not derive from private material. Human privacy, technical, and promotion review remain pull-request responsibilities. + +## Producing revisions and inputs + +- Graphcal: `0.0.1-alpha.23`, commit `04ab7150534de0b795acae1b3a7bcc282266ec3c` +- Corpus source revision: `42259833d1b8d6c60c761195a4a7981019058e17` +- Entrypoint: `src/pvwatts_hourly_dc_energy/main.gcl` +- Inputs: checked-in parameter defaults; no `--set` or `--input` overrides +- Expected output: `expected/four-hour-profile.json` +- Canonical expected-output SHA-256: `0b9d5330bcb9ae8cdb30f9971b97a0420e518fe72c05a676b85692bcba087a44` + +The source equation is A. P. Dobos, *PVWatts Version 5 Manual*, NREL/TP-6A20-62641, September 2014, Section 10, Equation 8, DOI [10.2172/1158421](https://doi.org/10.2172/1158421). That citation motivates the model but does not supply its expected values. + +The baseline was generated from the project directory with: + +```console +graphcal eval --format json --root . src/pvwatts_hourly_dc_energy/main.gcl \ + | jq -S . > expected/four-hour-profile.json +``` + +## Comparison rules + +The runner parses both outputs as strict JSON and compares them semantically. Object key order is ignored; array order and length are significant; missing or additional values fail; and JSON values compare exactly by type and value. This baseline declares no numeric tolerances. + +Exact comparison intentionally detects deterministic changes across indexed calculations, temperature differences, aggregation, and custom energy-unit display. It does not prove that the synthetic operating profile or its outputs match a published PV system. + +## Repeated-run determinism + +Five consecutive evaluations with the recorded Graphcal executable and source revision produced the same canonical semantic JSON. Each result was canonicalized with `jq -S -c` and hashed with SHA-256: + +```text +0b9d5330bcb9ae8cdb30f9971b97a0420e518fe72c05a676b85692bcba087a44 +0b9d5330bcb9ae8cdb30f9971b97a0420e518fe72c05a676b85692bcba087a44 +0b9d5330bcb9ae8cdb30f9971b97a0420e518fe72c05a676b85692bcba087a44 +0b9d5330bcb9ae8cdb30f9971b97a0420e518fe72c05a676b85692bcba087a44 +0b9d5330bcb9ae8cdb30f9971b97a0420e518fe72c05a676b85692bcba087a44 +``` + +This establishes repeatability only for the recorded executable and inputs. diff --git a/projects/pvwatts-hourly-dc-energy/src/pvwatts_hourly_dc_energy/main.gcl b/projects/pvwatts-hourly-dc-energy/src/pvwatts_hourly_dc_energy/main.gcl new file mode 100644 index 0000000..ada4d3c --- /dev/null +++ b/projects/pvwatts-hourly-dc-energy/src/pvwatts_hourly_dc_energy/main.gcl @@ -0,0 +1,37 @@ +const unit kWh: Energy = 1.0 kW * h; + +pub index Period = { LowSun, MidMorning, SolarNoon, Afternoon }; + +param nameplate_dc_power: Power = 5.0 kW; +param effective_irradiance: Power / Area[Period] = { + Period.LowSun: 200.0 W/m^2, + Period.MidMorning: 600.0 W/m^2, + Period.SolarNoon: 1000.0 W/m^2, + Period.Afternoon: 400.0 W/m^2, +}; +param cell_temperature: Temperature[Period] = { + Period.LowSun: 288.15 K, + Period.MidMorning: 298.15 K, + Period.SolarNoon: 318.15 K, + Period.Afternoon: 308.15 K, +}; +param temperature_coefficient: Temperature^-1 = -0.004 1/K; +param interval_duration: Time = 1.0 h; + +const node reference_irradiance: Power / Area = 1000.0 W/m^2; +const node reference_cell_temperature: Temperature = 298.15 K; + +node temperature_factor: Dimensionless[Period] = for period: Period { + 1.0 + @temperature_coefficient * (@cell_temperature[period] - @reference_cell_temperature) +}; +node dc_power: Power[Period] = for period: Period { + @nameplate_dc_power * ( + @effective_irradiance[period] / @reference_irradiance + ) * @temperature_factor[period] +}; +node interval_dc_energy: Energy[Period] = for period: Period { + @dc_power[period] * @interval_duration +}; +node low_sun_dc_power: Power = @dc_power[Period.LowSun] -> kW; +node peak_dc_power: Power = maximum(@dc_power) -> kW; +node total_dc_energy: Energy = sum(@interval_dc_energy) -> kWh; diff --git a/schema/README.md b/schema/README.md index 017c176..8906afd 100644 --- a/schema/README.md +++ b/schema/README.md @@ -98,9 +98,13 @@ kind = "reference-backed" evidence = "reference/calculation.md" [[projects.cases.expectation.assertions]] -pointer = "/node/mass_ratio/si_value" -expected = 10.0 -absolute_tolerance = 1.0 +pointer = "/node/reported_reference_evapotranspiration/display_value" +expected = 3.9 +absolute_tolerance = 1e-12 + +[[projects.cases.expectation.assertions]] +pointer = "/node/reported_reference_evapotranspiration/unit" +expected = "mm_per_day" ``` A reference-backed expectation requires: @@ -110,7 +114,7 @@ A reference-backed expectation requires: `expected` is a JSON scalar representable in TOML: boolean, integer, float, or string. Numeric assertions use optional non-negative finite `absolute_tolerance` and `relative_tolerance` values, both zero by default. Non-numeric assertions are exact and cannot declare a positive tolerance. -The checked-in evidence must identify the source, assumptions, adaptations, conventions, independent calculation, and licensing status. The runner enforces the declared values but cannot establish that the evidence itself is trustworthy; human review remains required. +The checked-in evidence must identify the reliable public source that reports both the reproduced inputs and each asserted output. It must map every assertion to an exact example, page, table, test vector, or other source location; document assumptions, adaptations, conventions, source precision, rounding, unit conversions, and licensing status; and avoid asserting locally generated extra precision. A local analytical or independent calculation may corroborate the source but cannot be the sole oracle. If the source provides only an equation or method, use `stability-baseline` instead. The runner enforces the declared values but cannot establish that the evidence itself is trustworthy; human review remains required. ## Project files and paths