Skip to content

feat!: add support for real-world climate data and on-disk caching - #5556

Open
Wanes01 wants to merge 164 commits into
AlchemistSimulator:masterfrom
Wanes01:alchemist-geospatial
Open

Wanes01 wants to merge 164 commits into
AlchemistSimulator:masterfrom
Wanes01:alchemist-geospatial

Conversation

@Wanes01

@Wanes01 Wanes01 commented Aug 17, 2026

Copy link
Copy Markdown

No description provided.

@codecov

codecov Bot commented Aug 17, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 61.53%. Comparing base (7a9199b) to head (5e7f88a).

Additional details and impacted files
@@            Coverage Diff            @@
##             master    #5556   +/-   ##
=========================================
  Coverage     61.53%   61.53%           
  Complexity       14       14           
=========================================
  Files             2        2           
  Lines            78       78           
  Branches          4        4           
=========================================
  Hits             48       48           
  Misses           24       24           
  Partials          6        6           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@mergify

mergify Bot commented Aug 18, 2026 •

Copy link
Copy Markdown
Contributor

This pull request does not currently match the merge queue conditions, so it cannot be queued from here. The box comes back if it matches again.

@Wanes01
Wanes01 force-pushed the alchemist-geospatial branch from 4e73252 to 6da2fa4 Compare August 18, 2026 10:34

@DanySK DanySK left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are several critical issues that must be solved before I go ahead with the review. See comments.

Comment thread alchemist-full/build.gradle.kts Outdated
Comment thread alchemist-maps/build.gradle.kts Outdated
Comment thread build.gradle.kts Outdated
@DanySK
DanySK force-pushed the alchemist-geospatial branch 11 times, most recently from fbd2faa to 8337419 Compare August 20, 2026 20:26
@mergify

mergify Bot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Hi @Wanes01! 👋
This pull request has conflicts 😖
Could you fix it? 🔧
Thank you! 🙏

1 similar comment
@mergify

mergify Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Hi @Wanes01! 👋
This pull request has conflicts 😖
Could you fix it? 🔧
Thank you! 🙏

@Wanes01
Wanes01 force-pushed the alchemist-geospatial branch from 8e54f3b to 8a18c40 Compare September 17, 2026 17:08
@sonarqubecloud

Copy link
Copy Markdown

@mergify

mergify Bot commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

Hi @Wanes01! 👋
This pull request has conflicts 😖
Could you fix it? 🔧
Thank you! 🙏

@DanySK
DanySK force-pushed the alchemist-geospatial branch from 8a18c40 to 08289e1 Compare October 28, 2026 11:04
@DanySK DanySK changed the title feat(geospatial): add alchemist-geospatial module for real-world climate data feat!: add support for real-world climate data and on-disk caching Oct 28, 2026
@DanySK

DanySK commented Oct 28, 2026

Copy link
Copy Markdown
Member

@codex review

@DanySK
DanySK force-pushed the alchemist-geospatial branch from 4bff9bb to 6af35f1 Compare October 28, 2026 18:48
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-28T19:00:25.821003Z 6af35f1 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@DanySK DanySK left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review focused on correctness and idiomatic, readable Kotlin. One inline comment per issue.

private val incarnation = mockk<Incarnation<Any?, GeoPosition>>()

@Test
fun `OSMEnvironment pbf import and DoubleCopernicusLayer must coexist`() {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test looks redundant now: TestCopernicusExamples loads local-data.yml/.kts, which builds an OSMEnvironment from maps/cesena.pbf together with a DoubleCopernicusLayer and checks the perceived values, so the OSM + cdm-core coexistence is already exercised there through the DSL. This one does it by hand (MockK Incarnation, manual temp dir with try/finally, an empty line inside the body on line 51). I would remove it. If you keep it, build the scenario through the DSL and reuse the copernicus/glofas-cesena resource instead of writing a NetCDF file.

to download the data again, for instance because the data store published a revised version,
delete the corresponding entry from the cache directory.

### Other parameters

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

checkMd5 is listed under "Other parameters", which suggests it is optional, but it has no default in either class and must always be given (both examples pass checkMd5: true). Please say it is required, or give it a default in the code (see the comment on DoubleCopernicusLayer).

Data slices are timestamped with real-world instants, while simulations advance in simulation time units.
Two parameters, both written in ISO-8601, connect them:

* `timeScale` is the real-world duration of one simulation time unit (by default, `PT1H`: one hour);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The defaults described here (PT1H, default cache directory, ~/.cdsapirc, PT30M) exist only on DoubleCopernicusLayer. The generic CopernicusLayer, which line 165 recommends for other types, requires every parameter. Either qualify the defaults ("for DoubleCopernicusLayer") or move the defaults to CopernicusLayer (preferred, see the code comment).


## Interpolation and conversion

Named parameters are supported in both YAML and the Kotlin DSL;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This note about named parameters is general, not specific to interpolation/conversion, but it sits under "Interpolation and conversion", while the place where users hit it is the datastore example (timeOrigin: null, variable: null just to reach cacheDirectory). Suggest moving it next to the datastore example or into the "Other parameters" section. Small wording fix on line 63: "request the data directly from a data store".

All the files in the directory must hold the same variable on the same grid,
and their time slices get merged in chronological order
(if several files hold the same instant, the slice of the first file is used).
The directory can be a path on the file system or the name of a directory among the classpath resources.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"The name of a directory among the classpath resources" only works when resources are unpacked on disk (e.g. build/resources). From inside a JAR, Path.of(jar: URI) fails (see the comment on CopernicusLayer.localPath). Please qualify this, or fix the code.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review


P1 Badge Remove generated GRIB index artifacts

The added .gbx9 and .ncx4 files are NetCDF-Java-generated indexes (the reader itself identifies these suffixes as indexes), and they embed the creator-specific path /home/wanes/Desktop/.../fc.grib. Keeping them in test resources makes the fixture machine-specific and risks CDM consuming stale index metadata; delete both indexes and let tests regenerate them under build output.

AGENTS.md reference: AGENTS.md:L44-L44

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +289 to +290
val request = CopernicusRequest(endpoint.trim().trimEnd('/'), dataset, inputs)
return FileSystemDirectoryCache(provider, cacheDirectoryRoot)[request]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve MD5 verification state in cached datasets

When the same shared cache is first populated with checkMd5=false (or that loader wins a concurrent race), this request receives exactly the same cache entry as a later checkMd5=true load. FileSystemDirectoryCache returns that existing directory without invoking the provider, so the later load silently skips its requested checksum verification; even an asset with the advertised size but bad contents, or one whose advertised checksum should fail, is treated as verified. Preserve verification metadata and upgrade/reload unverified hits, or otherwise keep the two cache states distinct.

Useful? React with 👍 / 👎.

* @throws IllegalArgumentException if [path] is neither an existing path nor a classpath resource.
*/
private fun localPath(path: String): Path = Path.of(path).takeIf { it.exists() }
?: ResourceLoader.getResource(path)?.let { Path.of(it.toURI()) }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Materialize packaged classpath resources before using Paths

When dataDirectory or inputsFile is a classpath resource inside a packaged JAR, ResourceLoader returns a jar: URL and Path.of(it.toURI()) throws FileSystemNotFoundException unless that JAR filesystem was explicitly mounted. Thus the newly advertised classpath-resource flow works in the exploded Gradle test classpath but fails in packaged distributions; copy the resource to a real directory/file or explicitly manage the archive filesystem.

Useful? React with 👍 / 👎.

Comment on lines +315 to +319
.uri(URI.create(url))
.header("PRIVATE-TOKEN", token)
.header("Accept", APPLICATION_JSON)
.GET()
.build(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Apply the deadline to polling HTTP requests

When a monitor endpoint accepts the connection but never completes its response, this request has no HttpRequest.timeout, so http.send can block indefinitely and the deadline check in awaitSuccess is never reached. The configured provider timeout therefore is not a guillotine for a stalled poll; each polling request should be bounded by the remaining overall timeout.

Useful? React with 👍 / 👎.

Comment on lines +60 to +63
val previous = System.getProperty(SAX_PARSER_FACTORY_KEY)
System.setProperty(SAX_PARSER_FACTORY_KEY, JDK_XERCES_SAX_PARSER_FACTORY)
try {
return NetcdfDatasets.openDataset(file.toString())

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Synchronize the global SAX parser override

When two EagerGridSnapshots instances open files concurrently, these save/set/restore operations race on a process-wide system property: one call can restore the original parser while the other is still opening a GRIB, and the later call can finally leave the JDK parser installed instead of the original value. This can both break the concurrent GRIB read and corrupt SAX configuration for unrelated code, so the entire override/open/restore sequence needs a shared lock (or a non-global parser configuration).

Useful? React with 👍 / 👎.

check(deadline.hasNotPassedNow()) {
"Timeout ($timeout) while waiting for job completion at $monitorUrl"
}
Thread.sleep(interval.inWholeMilliseconds)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Cap poll sleeps at the remaining deadline

When timeout is shorter than the current backoff interval, the code checks the deadline and then sleeps for the full interval; for example, a 500 ms timeout with the default 2 s interval waits 2 s and issues another GET before it can fail. This makes the configured timeout overrun by up to maxPollInterval even with responsive servers, so sleep only for the remaining duration and check the deadline before the next request.

Useful? React with 👍 / 👎.

this.size <= 1 -> true
else -> {
for (i in 0 until this.size - 1) {
if (this[i] >= this[i + 1]) return false

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject NaN coordinates in raster axes

When either axis contains NaN, the >= comparison is false rather than detecting an ordering violation (and arrays of length one bypass the loop entirely), so construction accepts an axis that is not strictly ordered. Later range checks and binary searches then reject valid positions or select bogus cells instead of failing at ingestion; validate each coordinate as non-NaN while checking adjacent values.

Useful? React with 👍 / 👎.

@DanySK
DanySK force-pushed the alchemist-geospatial branch from 6af35f1 to e3ea446 Compare October 29, 2026 12:04
DanySK added 18 commits October 29, 2026 13:22
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants