Compare commits

...

282 Commits
v2.1.0 ... main

Author SHA1 Message Date
semantic-release-bot
a38dba5728 chore(release): 6.0.0
# [6.0.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.13.0...v6.0.0) (2026-01-19)

### Bug Fixes

* Update Arome to translate latlon to lambert ([655abeb](655abebe52))
2026-01-19 14:13:19 +00:00
083e5dac5b Merge branch 'simkir/arome' into 'main'
Update Arome for new rossby archives

See merge request oceanbox/Oceanbox.FvcomKit!35
2026-01-19 15:10:29 +01:00
622d9837fd Prerelease 6.0.0-alpha.1 2026-01-07 09:34:08 +01:00
655abebe52 fix: Update Arome to translate latlon to lambert
Also:
- Add test for reading uv from tile coords
- Build with nix
- Pin nix with npins
- Remove .config tools manifest
- Remove preview flag
2026-01-06 10:23:49 +01:00
18a9d70698 breaking: Do not project Arome.tryFind
Do that with tryFindWithProj, where you give the projection yourself,
instead.
2025-12-17 17:40:12 +01:00
semantic-release-bot
aaa597c52e chore(release): 5.13.0
# [5.13.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.12.2...v5.13.0) (2025-11-05)

### Features

* Add ElemsAroundElem to NeighborIndex ([71e861e](71e861e417))
2025-11-05 11:09:46 +00:00
Stig Rune Jensen
571302a86b Merge branch 'mrtz/e2e' into 'main'
feat: Add ElemsAroundElem to NeighborIndex

See merge request oceanbox/Oceanbox.FvcomKit!34
2025-11-05 11:07:03 +00:00
71e861e417 feat: Add ElemsAroundElem to NeighborIndex
Also format with Fantomas
2025-11-05 11:39:57 +01:00
semantic-release-bot
bec03ed5ec chore(release): 5.12.2
## [5.12.2](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.12.1...v5.12.2) (2025-09-01)

### Bug Fixes

* Bump Oceanbox.SDSLite to 2.8.0 and use bun for SR ([0c55b2d](0c55b2dcbd))
2025-09-01 13:32:02 +00:00
75ba2abcfe Merge branch 'mrtz/bump-sdslite' into 'main'
fix: Bump Oceanbox.SDSLite to 2.8.0 and use bun for SR

See merge request oceanbox/Oceanbox.FvcomKit!33
2025-09-01 15:28:05 +02:00
0c55b2dcbd fix: Bump Oceanbox.SDSLite to 2.8.0 and use bun for SR 2025-09-01 15:20:16 +02:00
semantic-release-bot
c9b3320464 chore(release): 5.12.1
## [5.12.1](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.12.0...v5.12.1) (2025-05-02)

### Bug Fixes

* include edge point in isInsideTriangle ([c89f35b](c89f35bc6e))
* remove unused double precision function ([8715f4d](8715f4d8c3))
2025-05-02 13:09:26 +00:00
Stig Rune Jensen
bfb3a8e489 Merge branch 'fix/edge-point-in-cell' into 'main'
fix: include edge point in isInsideTriangle

See merge request oceanbox/Oceanbox.FvcomKit!32
2025-05-02 13:06:29 +00:00
8715f4d8c3 fix: remove unused double precision function 2025-05-02 14:36:56 +02:00
c89f35bc6e fix: include edge point in isInsideTriangle
if a point is calculated to be exactly on a triangle edge, consider it
inside the cell
2025-05-02 14:14:26 +02:00
semantic-release-bot
c22ae77301 chore(release): 5.12.0
# [5.12.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.11.0...v5.12.0) (2025-03-07)

### Features

* add Grid.toLonLat function ([06a4aea](06a4aeabf1))
2025-03-07 07:02:32 +00:00
c4513c0b09 Merge branch 'main' of gitlab.com:oceanbox/Oceanbox.FvcomKit 2025-03-07 07:59:32 +01:00
06a4aeabf1 feat: add Grid.toLonLat function 2025-03-07 07:59:21 +01:00
semantic-release-bot
65fbf66016 chore(release): 5.11.0
# [5.11.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.10.0...v5.11.0) (2025-03-06)

### Bug Fixes

* init sha once explicitly from sha1 byte[] ([33b7b99](33b7b999c8))

### Features

* rename grid sha1 to hash for better generality ([bfbaa3a](bfbaa3aeff))
2025-03-06 07:58:43 +00:00
bfbaa3aeff feat: rename grid sha1 to hash for better generality 2025-03-06 08:55:49 +01:00
ad148f6284 Merge branch 'main' of gitlab.com:oceanbox/Oceanbox.FvcomKit 2025-03-06 08:53:15 +01:00
33b7b999c8 fix: init sha once explicitly from sha1 byte[] 2025-03-06 08:53:07 +01:00
semantic-release-bot
80f538fcb0 chore(release): 5.10.0
# [5.10.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.9.1...v5.10.0) (2025-03-06)

### Features

* init sha once explicitly from bingrid ([2e87294](2e87294c46))
2025-03-06 07:50:36 +00:00
6b16e07adc Merge branch 'main' of gitlab.com:oceanbox/Oceanbox.FvcomKit 2025-03-06 08:47:42 +01:00
2e87294c46 feat: init sha once explicitly from bingrid 2025-03-06 08:47:37 +01:00
semantic-release-bot
76cd56e1ab chore(release): 5.9.1
## [5.9.1](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.9.0...v5.9.1) (2025-03-06)

### Bug Fixes

* use sha from bingrid if it exists ([abdb949](abdb949113))
2025-03-06 07:42:01 +00:00
47279b49cb Merge branch 'main' of gitlab.com:oceanbox/Oceanbox.FvcomKit 2025-03-06 08:39:07 +01:00
abdb949113 fix: use sha from bingrid if it exists 2025-03-06 08:38:58 +01:00
semantic-release-bot
5464bc34f8 chore(release): 5.9.0
# [5.9.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.8.0...v5.9.0) (2025-03-06)

### Features

* add ToGrid() method to extended grid ([21e84d0](21e84d07cd))
2025-03-06 07:28:17 +00:00
3086ef5ebf Merge branch 'main' of gitlab.com:oceanbox/Oceanbox.FvcomKit 2025-03-06 08:25:16 +01:00
21e84d07cd feat: add ToGrid() method to extended grid 2025-03-06 08:25:09 +01:00
semantic-release-bot
376891e4a0 chore(release): 5.8.0
# [5.8.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.7.0...v5.8.0) (2025-03-06)

### Features

* compute sha1 checksum of extended grids ([bf7ae58](bf7ae5889b))
2025-03-06 06:55:14 +00:00
f19a74fc9d devel: update build tools 2025-03-06 07:37:39 +01:00
cef71eb90a devel: rename x. to this. 2025-03-06 07:36:55 +01:00
222b022662 devel: format with fantomas 2025-03-06 07:33:13 +01:00
bf7ae5889b feat: compute sha1 checksum of extended grids 2025-03-06 07:28:32 +01:00
semantic-release-bot
817c02b5a6 chore(release): 5.7.0
# [5.7.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.6.0...v5.7.0) (2025-02-12)

### Features

* add read omega block ([9abd9d4](9abd9d45bc))
2025-02-12 15:31:13 +00:00
Stig Rune Jensen
86e1307d79 Merge branch 'feat/omega-block' into 'main'
feat: add read omega block

See merge request oceanbox/Oceanbox.FvcomKit!31
2025-02-12 15:27:54 +00:00
Stig Rune Jensen
9abd9d45bc feat: add read omega block 2025-02-12 16:23:44 +01:00
semantic-release-bot
02de20d50b chore(release): 5.6.0
# [5.6.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.5.5...v5.6.0) (2024-11-27)

### Features

* update to net9.0 ([17685ac](17685ac2cd))
2024-11-27 11:24:15 +00:00
Jonas Juselius
c8a838c1e6 Merge branch 'net9.0' into 'main'
feat: update to net9.0

See merge request oceanbox/Oceanbox.FvcomKit!30
2024-11-27 11:19:26 +00:00
Jonas Juselius
17685ac2cd feat: update to net9.0 2024-11-27 12:08:08 +01:00
semantic-release-bot
6662ad4ebf chore(release): 5.5.5
## [5.5.5](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.5.4...v5.5.5) (2024-11-27)

### Bug Fixes

* add velocity to nodal function ([ed0ac79](ed0ac797d3))
2024-11-27 11:06:10 +00:00
Jonas Juselius
46209553a8 Merge branch 'main' of gitlab.com:oceanbox/Oceanbox.FvcomKit 2024-11-27 12:01:37 +01:00
Jonas Juselius
ed0ac797d3 fix: add velocity to nodal function 2024-11-27 12:01:18 +01:00
semantic-release-bot
63dc1f2cca chore(release): 5.5.4
## [5.5.4](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.5.3...v5.5.4) (2024-05-01)

### Bug Fixes

* Norshelf filenames changed due to changes in Thredds ([0f5de91](0f5de91d39))
2024-05-01 10:50:46 +00:00
Jonas Juselius
0f5de91d39 fix: Norshelf filenames changed due to changes in Thredds 2024-05-01 12:47:17 +02:00
semantic-release-bot
f2730ad1e3 chore(release): 5.5.3
## [5.5.3](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.5.2...v5.5.3) (2024-02-23)

### Bug Fixes

* bug in isInsideTriangle ([8d7ab41](8d7ab4164c))
2024-02-23 13:58:37 +00:00
Stig Rune Jensen
ef172fa883 Merge branch 'fix/inside-triangle' into 'main'
fix: bug in isInsideTriangle

See merge request oceanbox/Oceanbox.FvcomKit!29
2024-02-23 13:55:26 +00:00
Stig Rune Jensen
8d7ab4164c fix: bug in isInsideTriangle 2024-02-23 14:05:42 +01:00
semantic-release-bot
ff283cd910 chore(release): 5.5.2
## [5.5.2](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.5.1...v5.5.2) (2024-01-04)

### Bug Fixes

* fix ci/cd deploy name ([35e1f43](35e1f43fc0))
* update ci/cd setup to v2 ([3f61f1d](3f61f1d1a9))
2024-01-04 08:14:44 +00:00
Jonas Juselius
35e1f43fc0 fix: fix ci/cd deploy name 2024-01-04 08:55:48 +01:00
Jonas Juselius
3f61f1d1a9 fix: update ci/cd setup to v2 2024-01-04 08:50:32 +01:00
semantic-release-bot
ac5aad659b chore(release): 5.5.1
## [5.5.1](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.5.0...v5.5.1) (2024-01-03)

### Bug Fixes

* bump to sdk_8 in shell.nix ([bc13065](bc130658e9))
* update package sdslite.oceanbox-2.7.3 ([aba0917](aba0917557))
2024-01-03 11:29:07 +00:00
Stig Rune Jensen
746f58b29e Merge branch 'sdslite' into 'main'
SDSLite.Oceanbox

See merge request oceanbox/Oceanbox.FvcomKit!28
2024-01-03 11:25:51 +00:00
Stig Rune Jensen
bc130658e9 fix: bump to sdk_8 in shell.nix 2024-01-03 12:17:22 +01:00
Stig Rune Jensen
aba0917557 fix: update package sdslite.oceanbox-2.7.3 2024-01-03 12:16:39 +01:00
semantic-release-bot
72a49a8039 chore(release): 5.5.0
# [5.5.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.4.1...v5.5.0) (2023-12-31)

### Bug Fixes

* update devcontainer to net8.0 ([d75db35](d75db35d8b))

### Features

* upgrade to net8.0 ([cfbf2b1](cfbf2b1d61))
2023-12-31 12:17:26 +00:00
Jonas Juselius
d75db35d8b fix: update devcontainer to net8.0 2023-12-31 13:12:30 +01:00
Jonas Juselius
94f5eb56a0 devel: remove ProjNet.FSharp submodule reference 2023-12-31 12:53:50 +01:00
Jonas Juselius
cfbf2b1d61 feat: upgrade to net8.0 2023-12-31 12:45:17 +01:00
semantic-release-bot
e09caf7a12 chore(release): 5.4.1
## [5.4.1](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.4.0...v5.4.1) (2023-10-04)

### Bug Fixes

* fix arome variable names ([0f2b696](0f2b69609c))
2023-10-04 12:02:42 +00:00
Jonas Juselius
0f2b69609c fix: fix arome variable names 2023-10-04 13:59:03 +02:00
semantic-release-bot
8152537ee7 chore(release): 5.4.0
# [5.4.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.3.0...v5.4.0) (2023-09-25)

### Bug Fixes

* bug in bbox center ([90b50c3](90b50c3c83))
* remove depricated grid.projection ([f38f06c](f38f06c936))
* remove print ([d2e362d](d2e362df20))
* rescale/translate grid ([5f5ad1b](5f5ad1b9e4))
* update ProjNet (error on unknown proj) ([5479c8b](5479c8b598))

### Features

* read tauc from fvcom file ([4ca7fff](4ca7fff09e))
2023-09-25 06:14:56 +00:00
Jonas Juselius
b1341581a1 Merge branch 'rescale-grid' into 'main'
Rescale grid

See merge request oceanbox/Oceanbox.FvcomKit!27
2023-09-25 06:11:39 +00:00
Stig Rune Jensen
d2e362df20 fix: remove print 2023-09-22 15:39:41 +02:00
Stig Rune Jensen
f38f06c936 fix: remove depricated grid.projection 2023-09-22 15:32:30 +02:00
Stig Rune Jensen
90b50c3c83 fix: bug in bbox center 2023-09-22 15:32:30 +02:00
Stig Rune Jensen
5f5ad1b9e4 fix: rescale/translate grid 2023-09-22 15:32:14 +02:00
Stig Rune Jensen
4ca7fff09e feat: read tauc from fvcom file 2023-09-22 15:32:14 +02:00
Stig Rune Jensen
7e9e8db9cb wip: rescaling and translating grid 2023-09-22 15:32:14 +02:00
Stig Rune Jensen
5479c8b598 fix: update ProjNet (error on unknown proj) 2023-09-22 15:32:11 +02:00
semantic-release-bot
de0a4a2751 chore(release): 5.3.0
# [5.3.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.2.0...v5.3.0) (2023-09-06)

### Features

* add functtions to read grid in lon-lat format ([902ac08](902ac080e9))
2023-09-06 14:27:03 +00:00
Jonas Juselius
902ac080e9 feat: add functtions to read grid in lon-lat format 2023-09-06 16:23:22 +02:00
semantic-release-bot
22b65f878a chore(release): 5.2.0
# [5.2.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.1.1...v5.2.0) (2023-08-28)

### Bug Fixes

* getTimeInDays returns single, not float ([434ab6d](434ab6d5f4))
* remove FsKdTree references for faster builds ([86384bf](86384bfd9f))
* update deps ([460f8f1](460f8f10eb))
* update deps ([bb57404](bb57404523))

### Features

* add getTimeInDays to Fvcom accessors ([302c4f1](302c4f1048))
* add getTimsSpanSinceStart function ([764bded](764bdedb45))
* add Singlular module for time-series (later add Plural) ([eb2a981](eb2a981576))
* read data in sigma layer blocks ([e7bb554](e7bb5540ef))
* read necessary grid prop from file ([ff947bb](ff947bb364))
* remove projection from grid(s), now in archmesiter ([36bd52d](36bd52d3f1))
* upgrade to net7.0 ([2be8397](2be8397297))
* working arome querying ([4556a9f](4556a9fd9a))
2023-08-28 07:29:44 +00:00
Jonas Juselius
c53c2b5b05 Merge branch 'arome' into 'main'
Arome functions for wind barbs

See merge request oceanbox/Oceanbox.FvcomKit!26
2023-08-28 07:24:40 +00:00
Jonas Juselius
86d250916e build: update Dockerfile to net7 (sic) 2023-08-28 08:55:34 +02:00
Simen Kirkvik
4556a9fd9a feat: working arome querying 2023-08-25 09:57:00 +02:00
Simen Kirkvik
b67653179f bump ProjNet.FSharp MET-LCC branch 2023-08-25 09:53:21 +02:00
Simen Kirkvik
5a1ffcdf36 remove use of grid tiles wide and tall
We're already inside the bounding box, so this should not be needed
2023-08-23 12:48:42 +02:00
Simen Kirkvik
540a913fb4 add projection to SquareGrid 2023-08-23 12:44:20 +02:00
Simen Kirkvik
4b889998ac save longitudes and latitudes directly instead 2023-08-22 12:07:23 +02:00
Simen Kirkvik
01183f7469 bump projnet.fsharp 2023-08-21 10:13:12 +02:00
Simen Kirkvik
d749f1b157 bump ProjNet.FSharp 2023-08-21 10:06:00 +02:00
Simen Kirkvik
1bba6431c4 try to do some bounds checking 2023-08-18 15:36:07 +02:00
Simen Kirkvik
29a7c6c148 the grid is square 2023-08-18 15:09:31 +02:00
Simen Kirkvik
672e52cb8b map arome coordinates to singles 2023-08-18 14:23:58 +02:00
Simen Kirkvik
f6ee353980 add first draft of reading uvs 2023-08-17 17:03:45 +02:00
Simen Kirkvik
0d0bf9e873 wip: start on arome helper functions and types 2023-08-17 16:42:32 +02:00
Jonas Juselius
eb2a981576 feat: add Singlular module for time-series (later add Plural) 2023-06-16 15:12:31 +02:00
Stig Rune Jensen
ff947bb364 feat: read necessary grid prop from file 2023-06-03 11:12:16 +02:00
Jonas Juselius
36bd52d3f1 feat: remove projection from grid(s), now in archmesiter 2023-05-27 08:06:54 +02:00
Jonas Juselius
48ea6dd573 devel: update nuget deps 2023-05-27 08:05:59 +02:00
Jonas Juselius
5511abbbfc devel: update .editorconfig for fantomas6 2023-05-27 07:59:03 +02:00
Jonas Juselius
460f8f10eb fix: update deps 2023-05-27 07:59:03 +02:00
Jonas Juselius
764bdedb45 feat: add getTimsSpanSinceStart function 2023-05-27 07:59:03 +02:00
Jonas Juselius
82b4ceea36 devel: fix log message for getTimeInDays 2023-05-27 07:59:03 +02:00
Jonas Juselius
434ab6d5f4 fix: getTimeInDays returns single, not float 2023-05-27 07:59:03 +02:00
Jonas Juselius
302c4f1048 feat: add getTimeInDays to Fvcom accessors 2023-05-27 07:59:03 +02:00
Stig Rune Jensen
e7bb5540ef feat: read data in sigma layer blocks 2023-05-27 07:59:03 +02:00
Jonas Juselius
bb57404523 fix: update deps 2023-03-10 16:03:32 +01:00
Jonas Juselius
86384bfd9f fix: remove FsKdTree references for faster builds 2023-02-27 13:21:39 +01:00
Jonas Juselius
2be8397297 feat: upgrade to net7.0 2023-02-26 21:13:42 +01:00
semantic-release-bot
e9f10c8f12 chore(release): 5.1.1
## [5.1.1](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.1.0...v5.1.1) (2023-02-01)

### Bug Fixes

* compute and not read center variables ([a6a8828](a6a8828ae9))
* compute and not read siglay_center ([a9d7662](a9d7662a2b))
2023-02-01 09:20:15 +00:00
Ole Anders Nøst
6881554909 Merge branch 'centervars' into 'main'
Centervars

See merge request oceanbox/Oceanbox.FvcomKit!25
2023-02-01 09:16:18 +00:00
Ole Anders Nøst
a6a8828ae9 fix: compute and not read center variables 2023-02-01 10:09:48 +01:00
Ole Anders Nøst
a9d7662a2b fix: compute and not read siglay_center 2023-01-27 10:36:35 +01:00
Ole Anders Nøst
774c840a84 wip: debugging siglayatcenter 2023-01-26 12:30:59 +01:00
Ole Anders Nøst
41841e0d2a wip: debugging siglayatcenter 2023-01-26 12:27:56 +01:00
Ole Anders Nøst
7b3a14e073 wip: siglay center 2023-01-26 11:59:53 +01:00
semantic-release-bot
b1a6225f5e chore(release): 5.1.0
# [5.1.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.0.3...v5.1.0) (2023-01-16)

### Bug Fixes

* update node in devcontainer ([347a730](347a730e13))

### Features

* add readOmega for vertical sigma velocity ([7d81ac7](7d81ac771a))
2023-01-16 14:07:32 +00:00
Jonas Juselius
bb73564a00 Merge branch 'read-omega' into 'main'
feat: add readOmega for vertical sigma velocity

See merge request oceanbox/Oceanbox.FvcomKit!24
2023-01-16 14:02:52 +00:00
Stig Rune Jensen
347a730e13 fix: update node in devcontainer 2023-01-16 08:21:06 +01:00
Stig Rune Jensen
7d81ac771a feat: add readOmega for vertical sigma velocity 2023-01-15 20:21:59 +01:00
semantic-release-bot
efeec1519e chore(release): 5.0.3
## [5.0.3](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.0.2...v5.0.3) (2023-01-02)

### Bug Fixes

* using S_rho in readVerticalGrid ([886234b](886234b165))
2023-01-02 14:45:38 +00:00
Ole Anders Nøst
886234b165 fix: using S_rho in readVerticalGrid 2023-01-02 15:41:56 +01:00
semantic-release-bot
bab002d1a6 chore(release): 5.0.2
## [5.0.2](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.0.1...v5.0.2) (2023-01-02)

### Bug Fixes

* use S_rho as vertical roms coordinate ([f851e54](f851e5411b))
2023-01-02 14:28:50 +00:00
Ole Anders Nøst
f851e5411b fix: use S_rho as vertical roms coordinate 2023-01-02 15:24:31 +01:00
semantic-release-bot
af4833b555 chore(release): 5.0.1
## [5.0.1](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.0.0...v5.0.1) (2022-12-19)

### Bug Fixes

* update ProjNet.FSharp ([0bf255d](0bf255d510))
2022-12-19 09:13:39 +00:00
Jonas Juselius
0bf255d510 fix: update ProjNet.FSharp 2022-12-19 10:10:06 +01:00
semantic-release-bot
ce8b1e593b chore(release): 5.0.0
# [5.0.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v4.6.0...v5.0.0) (2022-12-07)

### Bug Fixes

* update ProjNet.FSharp ([ab63c39](ab63c39993))
2022-12-07 15:17:10 +00:00
Jonas Juselius
13e689d68e Merge branch 'projnet' into 'main'
Projnet update

See merge request oceanbox/Oceanbox.FvcomKit!23
2022-12-07 15:13:27 +00:00
Stig Rune Jensen
3305417c4b major: remove depricated Drifter specific helper modules 2022-12-07 15:52:19 +01:00
Stig Rune Jensen
ab63c39993 fix: update ProjNet.FSharp 2022-12-07 15:52:19 +01:00
semantic-release-bot
1caa90c246 chore(release): 4.6.0
# [4.6.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v4.5.3...v4.6.0) (2022-12-02)

### Features

* add tryGetNode grid method ([8c1c3e7](8c1c3e750f))
2022-12-02 12:29:32 +00:00
Jonas Juselius
8c1c3e750f feat: add tryGetNode grid method 2022-12-02 13:26:00 +01:00
semantic-release-bot
23fea2fd82 chore(release): 4.5.3
## [4.5.3](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v4.5.2...v4.5.3) (2022-12-02)

### Bug Fixes

* make siglev/lay readers faster ([a2b6a3b](a2b6a3b04f))
2022-12-02 09:46:01 +00:00
Jonas Juselius
a2b6a3b04f fix: make siglev/lay readers faster 2022-12-02 10:41:39 +01:00
semantic-release-bot
116a714748 chore(release): 4.5.2
## [4.5.2](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v4.5.1...v4.5.2) (2022-11-16)

### Bug Fixes

* fix obc indexing off by one ([0bf1889](0bf18891b2))
2022-11-16 12:27:08 +00:00
Jonas Juselius
0bf18891b2 fix: fix obc indexing off by one 2022-11-16 13:23:31 +01:00
semantic-release-bot
2ae5666f01 chore(release): 4.5.1
## [4.5.1](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v4.5.0...v4.5.1) (2022-11-15)

### Bug Fixes

* update ProjNet.FSharp ([1939213](1939213ec4))
2022-11-15 12:46:09 +00:00
Jonas Juselius
1939213ec4 fix: update ProjNet.FSharp 2022-11-15 13:42:16 +01:00
semantic-release-bot
7164c1793a chore(release): 4.5.0
# [4.5.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v4.4.1...v4.5.0) (2022-11-03)

### Features

* add functions to ger number of sigmas ([7c977ab](7c977abaa3))
2022-11-03 14:09:42 +00:00
Jonas Juselius
7c977abaa3 feat: add functions to ger number of sigmas 2022-11-03 15:04:59 +01:00
semantic-release-bot
2b361738a6 chore(release): 4.4.1
## [4.4.1](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v4.4.0...v4.4.1) (2022-10-18)

### Bug Fixes

* store Fvcom field as single, but use as float ([8cd202a](8cd202a699))
2022-10-18 10:03:21 +00:00
Jonas Juselius
e7da5922cf Merge branch 'single-field' into 'main'
fix: store Fvcom field as single, but use as float

See merge request oceanbox/Oceanbox.FvcomKit!22
2022-10-18 09:59:36 +00:00
Stig Rune Jensen
8cd202a699 fix: store Fvcom field as single, but use as float 2022-10-18 11:16:42 +02:00
semantic-release-bot
603785f07e chore(release): 4.4.0
# [4.4.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v4.3.0...v4.4.0) (2022-10-17)

### Features

* add readUV(W) ranges ([e2c977b](e2c977b843))
2022-10-17 13:02:00 +00:00
Jonas Juselius
e2e2585861 Merge branch 'uvrange' into 'main'
feat: add readUV(W) ranges

See merge request oceanbox/Oceanbox.FvcomKit!21
2022-10-17 12:58:15 +00:00
Stig Rune Jensen
e2c977b843 feat: add readUV(W) ranges 2022-10-17 14:45:33 +02:00
semantic-release-bot
4c04182843 chore(release): 4.3.0
# [4.3.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v4.2.0...v4.3.0) (2022-10-12)

### Features

* compute circumscribed circle around element ([3c9a7a7](3c9a7a701e))
2022-10-12 10:58:52 +00:00
Jonas Juselius
c4b927553b Merge branch 'circumcircle' into 'main'
feat: compute circumscribed circle around element

See merge request oceanbox/Oceanbox.FvcomKit!20
2022-10-12 10:55:11 +00:00
Stig Rune Jensen
3c9a7a701e feat: compute circumscribed circle around element 2022-10-12 12:47:03 +02:00
semantic-release-bot
0e28771da2 chore(release): 4.2.0
# [4.2.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v4.1.0...v4.2.0) (2022-10-05)

### Features

* add Fvcom.getTime ([cbe31b9](cbe31b94b5))
2022-10-05 13:22:35 +00:00
Jonas Juselius
fda547eb18 Merge branch 'add-get-archive-time' into 'main'
Add Fvcom.getTime

See merge request oceanbox/Oceanbox.FvcomKit!19
2022-10-05 13:18:41 +00:00
Simen Kirkvik
cbe31b94b5 feat: add Fvcom.getTime
given a netcdf frame, return the date time
2022-10-05 13:59:42 +02:00
Jonas Juselius
b7c488af51 build: update submodules 2022-10-04 11:10:55 +02:00
Jonas Juselius
68caf51645 buidl: update deps 2022-10-04 10:45:08 +02:00
semantic-release-bot
218c4f81d7 chore(release): 4.1.0
# [4.1.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v4.0.0...v4.1.0) (2022-09-30)

### Features

* project on boundary ([0a8b31c](0a8b31c1f3))
2022-09-30 14:52:28 +00:00
Jonas Juselius
9e925df063 Merge branch 'field-evaluators' into 'main'
feature: 4D field evaluators

See merge request oceanbox/Oceanbox.FvcomKit!18
2022-09-30 14:48:37 +00:00
Stig Rune Jensen
d980217b3f Generalize sigma and time interpolation 2022-09-30 15:50:07 +02:00
Stig Rune Jensen
8190554923 More clean up files 2022-09-30 15:50:07 +02:00
Stig Rune Jensen
ba964dc7a4 Clean up files 2022-09-30 15:50:07 +02:00
Stig Rune Jensen
2a545be683 Separate CloughTocher file 2022-09-30 15:50:05 +02:00
Stig Rune Jensen
7a8c31c79f Run fantomas 2022-09-30 15:49:18 +02:00
Stig Rune Jensen
5897cae4ad Introduce Field4D 2022-09-30 15:49:18 +02:00
Stig Rune Jensen
029e59dd86 Add time interpolation 2022-09-30 15:49:18 +02:00
Stig Rune Jensen
4d28dd09d4 Reintroduce divert on boundary 2022-09-30 15:49:18 +02:00
Stig Rune Jensen
1cab11cb3e Add constant and linear interpolation in 3D 2022-09-30 15:49:18 +02:00
Stig Rune Jensen
bcaca0cd2f wip: more 3D features 2022-09-30 15:49:18 +02:00
Stig Rune Jensen
98e52e7c42 wip: adding 3D features 2022-09-30 15:49:18 +02:00
Stig Rune Jensen
0a8b31c1f3 feat: project on boundary 2022-09-30 15:49:18 +02:00
semantic-release-bot
49c1c25ee5 chore(release): 4.0.0
# [4.0.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.14.0...v4.0.0) (2022-09-17)

### Bug Fixes

* reformat src with fantoms ([7955a76](7955a76178))
2022-09-17 07:17:24 +00:00
Jonas Juselius
7955a76178 fix: reformat src with fantoms 2022-09-15 10:11:07 +02:00
Jonas Juselius
b6cdd4f2f2 major: use double precision for grids and extended grids 2022-09-15 10:10:44 +02:00
semantic-release-bot
9f8deb93de chore(release): 3.14.0
# [3.14.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.13.0...v3.14.0) (2022-09-12)

### Features

* add readUVW(s) and fix bug in readUVs ([80b2651](80b2651f24))
2022-09-12 09:19:17 +00:00
Jonas Juselius
1b6c6a37bf Merge branch 'getUVW' into 'main'
feat: add readUVW(s) and fix bug in readUVs

See merge request oceanbox/Oceanbox.FvcomKit!17
2022-09-12 09:15:34 +00:00
Stig Rune Jensen
80b2651f24 feat: add readUVW(s) and fix bug in readUVs 2022-09-09 15:50:32 +02:00
semantic-release-bot
8906415812 chore(release): 3.13.0
# [3.13.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.12.2...v3.13.0) (2022-09-09)

### Features

* add elemental accessors for U/V and WW ([060c775](060c7753a2))
2022-09-09 13:02:47 +00:00
Jonas Juselius
060c7753a2 feat: add elemental accessors for U/V and WW 2022-09-09 14:59:04 +02:00
semantic-release-bot
ac949c0df8 chore(release): 3.12.2
## [3.12.2](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.12.1...v3.12.2) (2022-09-09)

### Bug Fixes

* update evaluateLinearCentroid to take a readUV fvcom function ([aab00e9](aab00e9651))
2022-09-09 12:39:17 +00:00
Jonas Juselius
dd8659a531 Merge branch 'evaluate-take-eidx' into 'main'
Update evaluateLinearCentroid to take a readUV fvcom function

See merge request oceanbox/Oceanbox.FvcomKit!16
2022-09-09 12:35:32 +00:00
Simen Kirkvik
aab00e9651 fix: update evaluateLinearCentroid to take a readUV fvcom function 2022-09-09 14:30:42 +02:00
semantic-release-bot
3ff48ceaf9 chore(release): 3.12.1
## [3.12.1](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.12.0...v3.12.1) (2022-09-09)

### Bug Fixes

* extract node element tree from option before saving to disk ([c6175f2](c6175f2e14))
2022-09-09 12:20:29 +00:00
Jonas Juselius
3e71d34bcb Merge branch 'evaluate-take-eidx' into 'main'
Fix node element tree file saving

See merge request oceanbox/Oceanbox.FvcomKit!15
2022-09-09 12:16:43 +00:00
Simen Kirkvik
c6175f2e14 fix: extract node element tree from option before saving to disk 2022-09-09 13:51:35 +02:00
Jonas Juselius
cf68ceda3b build: remove sdslite from submodules 2022-09-09 13:25:03 +02:00
semantic-release-bot
223078ad8b chore(release): 3.12.0
# [3.12.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.11.5...v3.12.0) (2022-09-09)

### Features

* Clough-Tocher interpolation ([8838018](8838018ee0))
2022-09-09 10:32:20 +00:00
Jonas Juselius
b4a2c27b26 Merge branch 'clough-tocher' into 'main'
Clough-Tocher interpolation

See merge request oceanbox/Oceanbox.FvcomKit!14
2022-09-09 10:28:28 +00:00
semantic-release-bot
e19cb18ecb chore(release): 3.11.5
## [3.11.5](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.11.4...v3.11.5) (2022-09-07)

### Bug Fixes

* don't use dotnet restore lock file ([b4500ce](b4500ceaa6))
* revert to C# kd-tree for now ([c857856](c857856b5e))
2022-09-07 17:07:44 +00:00
Jonas Juselius
dd7815e886 build: rename vendor to submodules 2022-09-07 19:03:44 +02:00
Jonas Juselius
c857856b5e fix: revert to C# kd-tree for now 2022-09-07 18:59:05 +02:00
Jonas Juselius
b4500ceaa6 fix: don't use dotnet restore lock file 2022-09-07 18:58:35 +02:00
Stig Rune Jensen
8838018ee0 feat: Clough-Tocher interpolation 2022-09-07 14:29:29 +02:00
Stig Rune Jensen
267de9b4be wip: compute vertex values once globally 2022-09-07 14:29:18 +02:00
Stig Rune Jensen
e3842e9d0b wip: first take Clough-Tocher
Seems to works, but ded slow
2022-09-07 14:29:05 +02:00
semantic-release-bot
126180f40d chore(release): 3.11.4
## [3.11.4](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.11.3...v3.11.4) (2022-09-06)

### Bug Fixes

* update vendor dependencies ([5c84402](5c844026b6))
2022-09-06 18:02:14 +00:00
Jonas Juselius
5c844026b6 fix: update vendor dependencies 2022-09-06 19:58:05 +02:00
semantic-release-bot
39c2350deb chore(release): 3.11.3
## [3.11.3](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.11.2...v3.11.3) (2022-09-06)

### Bug Fixes

* reenable FsKdTree (test) ([856fdbe](856fdbee40))
* update ProjNet.FSharp ([8e93280](8e9328098b))
* update ProjNet.FSharp ([3345ce6](3345ce6fbd))
2022-09-06 16:11:44 +00:00
Jonas Juselius
8e9328098b fix: update ProjNet.FSharp 2022-09-06 18:07:02 +02:00
Jonas Juselius
856fdbee40 fix: reenable FsKdTree (test) 2022-09-06 18:02:55 +02:00
Jonas Juselius
001eede4e5 devel: add FsKdTree as submodule 2022-09-06 17:58:48 +02:00
Jonas Juselius
3345ce6fbd fix: update ProjNet.FSharp 2022-09-06 17:52:27 +02:00
semantic-release-bot
8665dc5f90 chore(release): 3.11.2
## [3.11.2](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.11.1...v3.11.2) (2022-09-06)

### Bug Fixes

* submodules use https ([8782ca4](8782ca450c))
* submodules use relpath ([135def2](135def2573))
* use local vendor folder with git submodules ([7c22c3b](7c22c3bdc3))
2022-09-06 13:08:47 +00:00
Jonas Juselius
135def2573 fix: submodules use relpath 2022-09-06 15:05:13 +02:00
Jonas Juselius
8782ca450c fix: submodules use https 2022-09-06 14:06:32 +02:00
Jonas Juselius
ff7a0b65cd Merge branch 'main' of gitlab.com:oceanbox/Oceanbox.FvcomKit 2022-09-06 13:46:45 +02:00
Jonas Juselius
7c22c3bdc3 fix: use local vendor folder with git submodules 2022-09-06 13:45:52 +02:00
semantic-release-bot
f59e2b627f chore(release): 3.11.1
## [3.11.1](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.11.0...v3.11.1) (2022-09-06)

### Bug Fixes

* remove Sdk.Web dependency ([9fa5a37](9fa5a37709))
2022-09-06 10:26:02 +00:00
Jonas Juselius
9fa5a37709 fix: remove Sdk.Web dependency 2022-09-06 12:22:43 +02:00
Ole Anders Nøst
0dc63442aa Merge branch 'kdbug' into 'main'
debug: try with different KdTree

See merge request oceanbox/Oceanbox.FvcomKit!13
2022-09-06 07:18:18 +00:00
Jonas Juselius
a51c60bc3a debug: try with different KdTree 2022-09-05 13:47:03 +02:00
Ole Anders Nøst
017b6f3da3 Merge branch 'dev' into 'main'
Dev

See merge request oceanbox/Oceanbox.FvcomKit!12
2022-09-05 07:59:06 +00:00
Ole Anders Nøst
f8fce6fe87 Dev 2022-09-05 07:59:06 +00:00
Ole Anders Nøst
49a7fb384a Merge branch 'dev' into 'main'
Dev

See merge request oceanbox/Oceanbox.FvcomKit!11
2022-09-05 07:40:24 +00:00
Ole Anders Nøst
989f2c71c7 Dev 2022-09-05 07:40:24 +00:00
semantic-release-bot
e150b3fe48 chore(release): 3.11.0
# [3.11.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.10.4...v3.11.0) (2022-08-29)

### Features

* evaluate 2D fields in arbitrary points ([3d63732](3d63732450))
2022-08-29 17:32:02 +00:00
Jonas Juselius
68175bbdd1 Merge branch 'evaluate' into 'main'
feat: evaluate 2D fields in arbitrary points

See merge request oceanbox/Oceanbox.FvcomKit!10
2022-08-29 17:28:46 +00:00
Stig Rune Jensen
3d63732450 feat: evaluate 2D fields in arbitrary points
The function value F(x) at any point x is computed as a linear
extrapolation from the value F(x0) and gradient dF(x0) in the
nearest centroid position x0. The gradient is estimated from
the values in the nearest-neighbor cells.
2022-08-29 17:03:06 +02:00
semantic-release-bot
bccf7670b7 chore(release): 3.10.4
## [3.10.4](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.10.3...v3.10.4) (2022-08-29)

### Bug Fixes

* update deps ([9382b46](9382b46f38))
2022-08-29 11:15:10 +00:00
Jonas Juselius
9382b46f38 fix: update deps 2022-08-29 13:11:49 +02:00
Jonas Juselius
cddc518917 Merge branch 'interpolation' into 'main'
Interpolation

See merge request oceanbox/Oceanbox.FvcomKit!9
2022-08-29 10:34:23 +00:00
Ole Anders Nøst
8ae0793461 Interpolation 2022-08-29 10:34:23 +00:00
semantic-release-bot
f1f56e1305 chore(release): 3.10.3
## [3.10.3](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.10.2...v3.10.3) (2022-08-20)

### Bug Fixes

* do not calculate centroids on node tree ([7e41169](7e411693d5))
* do not save index tree as option ([8ba4de5](8ba4de5bb0))
2022-08-20 09:53:43 +00:00
Jonas Juselius
921bfbde0c Merge branch 'fixes' into 'main'
Several fixes

See merge request oceanbox/Oceanbox.FvcomKit!8
2022-08-20 09:49:35 +00:00
Simen Kirkvik
8ac41228a1 patch: make idx tree nearest node index
- add node index tree type
2022-08-18 09:34:32 +02:00
Simen Kirkvik
8ba4de5bb0 fix: do not save index tree as option
Also small refactorings
2022-08-18 09:29:30 +02:00
Simen Kirkvik
7e411693d5 fix: do not calculate centroids on node tree 2022-07-15 14:19:07 +02:00
semantic-release-bot
9f573c3506 chore(release): 3.10.2
## [3.10.2](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.10.1...v3.10.2) (2022-07-14)

### Bug Fixes

* bug in makeNestTree ([af26509](af26509fa4))
2022-07-14 11:35:34 +00:00
Jonas Juselius
d9b9cd2b0e Merge branch 'bugfix' into 'main'
Bugfix

See merge request oceanbox/Oceanbox.FvcomKit!7
2022-07-14 11:32:20 +00:00
Ole Anders N&st
9d87ea4001 Merge remote-tracking branch 'origin/main' into bugfix 2022-07-14 13:02:17 +02:00
Ole Anders N&st
e117e58bec separate corners and weights from interpolation 2022-07-11 10:24:26 +02:00
semantic-release-bot
611ecb88a7 chore(release): 3.10.1
## [3.10.1](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.10.0...v3.10.1) (2022-07-08)

### Bug Fixes

* Make tryFindElementTwice private not to confuse users ([75aa453](75aa45392f))
2022-07-08 09:14:19 +00:00
Jonas Juselius
75aa45392f fix: Make tryFindElementTwice private not to confuse users 2022-07-08 11:11:02 +02:00
semantic-release-bot
4d527a1891 chore(release): 3.10.0
# [3.10.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.9.0...v3.10.0) (2022-07-07)

### Features

* add methods to pickle and unpickle neighbor index ([252141f](252141fea0))
2022-07-07 17:42:48 +00:00
Jonas Juselius
252141fea0 feat: add methods to pickle and unpickle neighbor index 2022-07-07 19:39:32 +02:00
semantic-release-bot
f705661392 chore(release): 3.9.0
# [3.9.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.8.3...v3.9.0) (2022-07-07)

### Features

* switch to FsKDTree ([0ae2652](0ae2652fcc))
2022-07-07 14:31:43 +00:00
Jonas Juselius
6c5652ee68 Merge branch 'FsKDTree' into 'main'
Switch to FsKDTree

See merge request oceanbox/Oceanbox.FvcomKit!5
2022-07-07 14:28:27 +00:00
Simen Kirkvik
483622f121 bump FsKDTree 2022-07-07 16:15:37 +02:00
Simen Kirkvik
6d712b0928 update Adjoin.fs to use FsKDTree aswell 2022-07-07 15:42:42 +02:00
Simen Kirkvik
0ae2652fcc feat: switch to FsKDTree 2022-07-07 13:29:06 +02:00
Jonas Juselius
25666f1b87 Merge branch 'main' of gitlab.com:oceanbox/Oceanbox.FvcomKit 2022-07-06 12:54:12 +02:00
Jonas Juselius
e66964d8e0 ci: skip tests 2022-07-06 12:54:03 +02:00
semantic-release-bot
35d0f629db chore(release): 3.8.3
## [3.8.3](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.8.2...v3.8.3) (2022-07-06)
2022-07-06 09:23:19 +00:00
Jonas Juselius
753e72acae Merge branch 'tryGetElement-search-twice' into 'main'
On tryGetElement miss, try a second time on the surrounding elements

See merge request oceanbox/Oceanbox.FvcomKit!4
2022-07-06 09:18:05 +00:00
Simen Kirkvik
0b3f603e41 patch: on tryGetElement miss, try a second time on the surrounding elements 2022-07-05 12:36:24 +02:00
Ole Anders N&st
af26509fa4 fix: bug in makeNestTree 2022-07-04 17:08:40 +02:00
semantic-release-bot
f5ffc641c6 chore(release): 3.8.2
## [3.8.2](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.8.1...v3.8.2) (2022-06-23)

### Bug Fixes

* remove debug prints ([a5698ff](a5698ff89a))
2022-06-23 12:03:13 +00:00
Jonas Juselius
a5698ff89a fix: remove debug prints 2022-06-23 13:58:07 +02:00
semantic-release-bot
96f2888e58 chore(release): 3.8.1
## [3.8.1](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.8.0...v3.8.1) (2022-06-22)

### Bug Fixes

* fix insideTriangle (take 2) ([7efc6a1](7efc6a1d52))
2022-06-22 09:35:00 +00:00
Jonas Juselius
7efc6a1d52 fix: fix insideTriangle (take 2) 2022-06-22 11:29:48 +02:00
Jonas Juselius
ede1a3d3d5 refactor: rename findElement to tryFindElement 2022-06-21 15:54:04 +02:00
semantic-release-bot
2a418c35bb chore(release): 3.8.0
# [3.8.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.7.0...v3.8.0) (2022-06-20)

### Bug Fixes

* fix insideTriangle ([ea29fcb](ea29fcb601))

### Features

* add getNumFrames per Fvcom dataset archive ([ff268d4](ff268d461f))
2022-06-20 14:08:45 +00:00
Jonas Juselius
ff268d461f feat: add getNumFrames per Fvcom dataset archive 2022-06-20 16:03:40 +02:00
Jonas Juselius
ea29fcb601 fix: fix insideTriangle 2022-06-20 15:59:09 +02:00
semantic-release-bot
e6ba223354 chore(release): 3.7.0
# [3.7.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.6.0...v3.7.0) (2022-06-17)

### Features

* ExtendedGrid and refactor grids and helpers ([8143571](8143571a4b))
2022-06-17 14:22:05 +00:00
Jonas Juselius
dd85f6fb21 deps: bump ProjNet.FSharp 2022-06-17 15:49:54 +02:00
Jonas Juselius
bc9831c03f Merge branch 'main' of gitlab.com:oceanbox/Oceanbox.FvcomKit 2022-06-17 15:45:48 +02:00
Jonas Juselius
8143571a4b feat: ExtendedGrid and refactor grids and helpers 2022-06-17 15:44:44 +02:00
semantic-release-bot
2713e9d4af chore(release): 3.6.0
# [3.6.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.5.0...v3.6.0) (2022-06-16)

### Features

* add helpers to efficiently find nearest element and more ([225754b](225754bc4b))
2022-06-16 15:16:23 +00:00
Jonas Juselius
225754bc4b feat: add helpers to efficiently find nearest element and more 2022-06-16 17:11:01 +02:00
semantic-release-bot
13cc975ab7 chore(release): 3.5.0
# [3.5.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.4.0...v3.5.0) (2022-06-15)

### Bug Fixes

* track upstream ProjNet.FSharp changes ([1af2f32](1af2f322dd))

### Features

* add coordinate projection to grids ([7012d2b](7012d2b2ca))
2022-06-15 08:24:01 +00:00
Jonas Juselius
1af2f322dd fix: track upstream ProjNet.FSharp changes 2022-06-15 09:51:32 +02:00
Jonas Juselius
3f2fd296f6 Merge branch 'main' of gitlab.com:oceanbox/Oceanbox.FvcomKit 2022-06-15 09:23:06 +02:00
Jonas Juselius
7012d2b2ca feat: add coordinate projection to grids 2022-06-15 09:22:33 +02:00
Jonas Juselius
ddfca6eda1 build: use package references locally 2022-06-15 09:20:39 +02:00
semantic-release-bot
e43866d0bd chore(release): 3.4.0
# [3.4.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.3.0...v3.4.0) (2022-06-15)

### Features

* add routines to convert elemental to nodal props ([1c74935](1c749355fb))
2022-06-15 06:24:00 +00:00
Jonas Juselius
7dfa5831ab Merge branch 'nearest4' 2022-06-15 08:17:33 +02:00
Jonas Juselius
1c749355fb feat: add routines to convert elemental to nodal props 2022-06-15 08:16:30 +02:00
semantic-release-bot
db52e83a8f chore(release): 3.3.0
# [3.3.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.2.0...v3.3.0) (2022-06-09)

### Bug Fixes

* bug fix zeta getter ([b564dec](b564dec305))

### Features

* add getter for zeta ([db7fe87](db7fe87a5a))
2022-06-09 13:50:19 +00:00
Jonas Juselius
d0f7bd78ae Merge branch 'nearest4' into 'main'
Nearest4

See merge request oceanbox/Oceanbox.FvcomKit!3
2022-06-09 13:44:27 +00:00
Jonas Juselius
b564dec305 fix: bug fix zeta getter 2022-06-03 09:22:13 +02:00
Jonas Juselius
db7fe87a5a feat: add getter for zeta 2022-06-02 23:37:55 +02:00
semantic-release-bot
b0f45fbd86 chore(release): 3.2.0
# [3.2.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.1.0...v3.2.0) (2022-05-17)

### Bug Fixes

* refactor for better code structure ([b37d257](b37d257674))

### Features

* bilinear interpolation ([37613cb](37613cb19f))
2022-05-17 11:04:58 +00:00
Jonas Juselius
13b1e8f1a9 Merge branch 'nearest4' into 'main'
Horizontal interpolation and refactoring

See merge request oceanbox/Oceanbox.FvcomKit!2
2022-05-17 10:59:39 +00:00
Jonas Juselius
a1d4c69df8 Merge branch 'main' into 'nearest4'
# Conflicts:
#   src/Oceanbox.FvcomKit.fsproj
2022-05-17 10:54:56 +00:00
Jonas Juselius
b37d257674 fix: refactor for better code structure 2022-05-17 12:43:11 +02:00
Jonas Juselius
37613cb19f feat: bilinear interpolation 2022-05-17 11:57:18 +02:00
Jonas Juselius
5d8ea38b8f wip: upper left corner 2022-05-16 15:58:11 +02:00
Jonas Juselius
55abc1b51b wip: get quadrant 2022-05-16 14:20:21 +02:00
semantic-release-bot
cb72780e38 chore(release): 3.1.0
# [3.1.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.0.3...v3.1.0) (2022-05-11)

### Features

* Remove functions really belonging to Primus ([b43466f](b43466fb24))
2022-05-11 16:16:14 +00:00
Jonas Juselius
b569b497d0 Merge branch 'main' of gitlab.com:oceanbox/Oceanbox.FvcomKit 2022-05-11 18:09:39 +02:00
Jonas Juselius
b43466fb24 feat: Remove functions really belonging to Primus 2022-05-11 18:09:30 +02:00
semantic-release-bot
b8fdf3f2fd chore(release): 3.0.3
## [3.0.3](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.0.2...v3.0.3) (2022-05-11)

### Bug Fixes

* remove writeFvcomRestart ([f869d26](f869d2611b))
2022-05-11 15:58:11 +00:00
Jonas Juselius
f869d2611b fix: remove writeFvcomRestart 2022-05-11 17:48:48 +02:00
semantic-release-bot
7fef736cec chore(release): 3.0.2
## [3.0.2](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.0.1...v3.0.2) (2022-05-11)

### Bug Fixes

* add warning for empty kd-trees (input error) ([ecceccd](ecceccd297))
2022-05-11 15:27:41 +00:00
Jonas Juselius
ecceccd297 fix: add warning for empty kd-trees (input error) 2022-05-11 17:17:46 +02:00
semantic-release-bot
6810092602 chore(release): 3.0.1
## [3.0.1](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.0.0...v3.0.1) (2022-05-11)

### Bug Fixes

* generalize Grid projections ([7ebad27](7ebad27419))
* update deps ([aef93d2](aef93d2d4f))
2022-05-11 11:49:59 +00:00
Jonas Juselius
7ebad27419 fix: generalize Grid projections 2022-05-11 13:44:14 +02:00
Jonas Juselius
aef93d2d4f fix: update deps 2022-05-11 13:44:13 +02:00
semantic-release-bot
8eabb2b3bd chore(release): 3.0.0
# [3.0.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v2.1.0...v3.0.0) (2022-05-11)
2022-05-11 10:06:30 +00:00
Jonas Juselius
b8fd0e0a19 major: add coordinate projection support 2022-05-11 11:49:41 +02:00
43 changed files with 3862 additions and 11394 deletions

View File

@@ -11,23 +11,23 @@ let srcPath = Path.getFullName "src"
let testPath = Path.getFullName "test"
let libPath = Some srcPath
let deployPath = Path.getFullName "deploy"
let distPath = Path.getFullName "dist"
let packPath = Path.getFullName "packages"
let versionFile = Path.getFullName ".version"
Target.create "Clean" (fun _ -> Shell.cleanDir deployPath)
Target.create "Clean" (fun _ -> Shell.cleanDir distPath)
Target.create "InstallClient" (fun _ ->
run npm "install" "."
run bun "install" "."
run dotnet "tool restore" "."
)
Target.create "Bundle" (fun _ ->
run dotnet $"publish -c Release -o \"{deployPath}\"" srcPath
run dotnet $"publish -c Release -o \"{distPath}\"" srcPath
)
Target.create "BundleDebug" (fun _ ->
run dotnet $"publish -c Debug -o \"{deployPath}\"" srcPath
run dotnet $"publish -c Debug -o \"{distPath}\"" srcPath
)
Target.create "Pack" (fun _ ->

View File

@@ -72,16 +72,16 @@ let createProcess exe arg dir =
|> CreateProcess.ensureExitCode
let dotnet = createProcess "dotnet"
let npm =
let npmPath =
match ProcessUtils.tryFindFileOnPath "npm" with
let bun =
let bunPath =
match ProcessUtils.tryFindFileOnPath "bun" with
| Some path -> path
| None ->
"npm was not found in path. Please install it and make sure it's available from your path. " +
"bun was not found in path. Please install it and make sure it's available from your path. " +
"See https://safe-stack.github.io/docs/quickstart/#install-pre-requisites for more info"
|> failwith
createProcess npmPath
createProcess bunPath
let run proc arg dir =
proc arg dir

View File

@@ -1,18 +0,0 @@
{
"version": 1,
"isRoot": true,
"tools": {
"fable": {
"version": "3.7.0",
"commands": [
"fable"
]
},
"fantomas-tool": {
"version": "4.6.4",
"commands": [
"fantomas"
]
}
}
}

View File

@@ -1,27 +1,30 @@
FROM mcr.microsoft.com/dotnet/sdk:6.0
FROM mcr.microsoft.com/dotnet/sdk:9.0
# Add keys and sources lists
RUN curl -sL https://deb.nodesource.com/setup_14.x | bash
RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" \
| tee /etc/apt/sources.list.d/yarn.list
# Bun version
ARG BUN_INSTALL=/usr/local
ARG BUN_VERSION=bun-v1.2.16
# Install node, 7zip, yarn, git, process tools
RUN apt-get update \
&& apt-get install -y nodejs p7zip-full git procps ssh-client
&& apt-get install -y p7zip-full git procps ssh-client unzip
# Install Bun
RUN set -eux; \
curl -fsSL https://bun.sh/install > /usr/local/bin/install-bun \
&& chmod +x /usr/local/bin/install-bun \
&& /usr/local/bin/install-bun $BUN_VERSION debug-info
ENV BUN_INSTALL=/usr/local
# Clean up
RUN apt-get autoremove -y \
&& apt-get clean -y \
&& rm -rf /var/lib/apt/lists/*
# Install dotnet tools
RUN dotnet tool install fable -g
# Trouble brewing
RUN rm /etc/ssl/openssl.cnf
# add dotnet tools to path to pick up fake and paket installation
# Add dotnet tools to path to pick up fake and paket installation
ENV PATH="/root/.dotnet/tools:${PATH}"
# Copy endpoint specific user settings into container to specify

View File

@@ -7,26 +7,30 @@ charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = false
[*.js]
indent_size = 2
max_line_length= 80
[*.nix]
indent_size = 2
max_line_length= 80
[*.fs]
max_line_length=120
# Feliz style
fsharp_single_argument_web_mode=true
fsharp_space_before_colon=false
fsharp_max_if_then_else_short_width=60
fsharp_max_infix_operator_expression=50
fsharp_max_record_width=70
fsharp_max_record_number_of_items=1
fsharp_max_array_or_list_width=70
fsharp_max_array_or_list_number_of_items=1
fsharp_max_value_binding_width=70
fsharp_max_function_binding_width=40
fsharp_max_dot_get_expression_width=50
fsharp_multiline_block_brackets_on_same_column=true
fsharp_newline_between_type_definition_and_members=false
fsharp_max_elmish_width=40
fsharp_align_function_signature_to_indentation=false
fsharp_alternative_long_member_definitions=false
fsharp_multi_line_lambda_closing_newline=false
fsharp_disable_elmish_syntax=false
fsharp_keep_indent_in_branch=false
fsharp_blank_lines_around_nested_multiline_expressions=false
max_line_length= 120
fsharp_max_if_then_else_short_width = 60
fsharp_max_infix_operator_expression = 80
fsharp_space_before_uppercase_invocation = true
fsharp_blank_lines_around_nested_multiline_expressions = false
fsharp_newline_between_type_definition_and_members = false
fsharp_multiline_bracket_style = stroustrup
fsharp_multi_line_lambda_closing_newline = true
fsharp_array_or_list_multiline_formatter = character_width
fsharp_max_array_or_list_width = 70
fsharp_max_array_or_list_number_of_items = 3
fsharp_record_multiline_formatter = number_of_items
fsharp_max_record_number_of_items = 3
fsharp_max_record_width = 70

3
.envrc Normal file
View File

@@ -0,0 +1,3 @@
export NPINS_DIRECTORY="nix"
use_nix

3
.gitignore vendored
View File

@@ -15,3 +15,6 @@ deploy
.ionide/
*.db
build.fsx.lock
dist/
.direnv/
result*

View File

@@ -1,9 +1,11 @@
variables:
DEPLOY_NAME: default
DEPLOY_NAMESPACE: default
SDK_VERSION: 9.0
SKIP_TESTS: "true"
include:
- project: oceanbox/gitlab-ci
ref: main
ref: v4.1
file: DotnetPackage.gitlab-ci.yml
inputs:
project-name: oceanbox.fvcomkit
project-dir: .

View File

@@ -1 +1 @@
2.1.0
6.0.0

View File

@@ -1,17 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Compile Include=".build/Helpers.fs" />
<Compile Include=".build/Build.fs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Fake.Core.Target" Version="5.22.0" />
<PackageReference Include="Fake.DotNet.Cli" Version="5.22.0" />
<PackageReference Include="Fake.IO.FileSystem" Version="5.22.0" />
<PackageReference Include="Farmer" Version="1.6.34" />
<PackageReference Update="FSharp.Core" Version="6.0.4-beta.22181.2" />
<PackageReference Include="Fake.Core.Target" Version="6.1.3" />
<PackageReference Include="Fake.DotNet.Cli" Version="6.1.3" />
<PackageReference Include="Fake.IO.FileSystem" Version="6.1.3" />
<PackageReference Include="Farmer" Version="1.9.11" />
<PackageReference Update="FSharp.Core" Version="9.0.201" />
</ItemGroup>
</Project>
</Project>

View File

@@ -1,77 +0,0 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27004.2005
MinimumVisualStudioVersion = 15.0.26124.0
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{C29C6F32-3A30-4071-9B4A-8FBCAAA5993A}"
ProjectSection(SolutionItems) = preProject
README.md = README.md
LICENSE = LICENSE
Dockerfile = Dockerfile
EndProjectSection
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Build", "Build.fsproj", "{C6824583-FB68-4F69-8117-6B29637A3B96}"
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "src", "src/Oceanbox.FvcomKit.fsproj", "{662A0CDC-7E82-4157-AD25-469DD7ABAA69}"
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "test", "test\Tests.fsproj", "{BCBD73E2-7170-4A85-BFD8-B76F056D4D49}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C6824583-FB68-4F69-8117-6B29637A3B96}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C6824583-FB68-4F69-8117-6B29637A3B96}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C6824583-FB68-4F69-8117-6B29637A3B96}.Debug|x64.ActiveCfg = Debug|Any CPU
{C6824583-FB68-4F69-8117-6B29637A3B96}.Debug|x64.Build.0 = Debug|Any CPU
{C6824583-FB68-4F69-8117-6B29637A3B96}.Debug|x86.ActiveCfg = Debug|Any CPU
{C6824583-FB68-4F69-8117-6B29637A3B96}.Debug|x86.Build.0 = Debug|Any CPU
{C6824583-FB68-4F69-8117-6B29637A3B96}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C6824583-FB68-4F69-8117-6B29637A3B96}.Release|Any CPU.Build.0 = Release|Any CPU
{C6824583-FB68-4F69-8117-6B29637A3B96}.Release|x64.ActiveCfg = Release|Any CPU
{C6824583-FB68-4F69-8117-6B29637A3B96}.Release|x64.Build.0 = Release|Any CPU
{C6824583-FB68-4F69-8117-6B29637A3B96}.Release|x86.ActiveCfg = Release|Any CPU
{C6824583-FB68-4F69-8117-6B29637A3B96}.Release|x86.Build.0 = Release|Any CPU
{662A0CDC-7E82-4157-AD25-469DD7ABAA69}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{662A0CDC-7E82-4157-AD25-469DD7ABAA69}.Debug|Any CPU.Build.0 = Debug|Any CPU
{662A0CDC-7E82-4157-AD25-469DD7ABAA69}.Debug|x64.ActiveCfg = Debug|Any CPU
{662A0CDC-7E82-4157-AD25-469DD7ABAA69}.Debug|x64.Build.0 = Debug|Any CPU
{662A0CDC-7E82-4157-AD25-469DD7ABAA69}.Debug|x86.ActiveCfg = Debug|Any CPU
{662A0CDC-7E82-4157-AD25-469DD7ABAA69}.Debug|x86.Build.0 = Debug|Any CPU
{662A0CDC-7E82-4157-AD25-469DD7ABAA69}.Release|Any CPU.ActiveCfg = Release|Any CPU
{662A0CDC-7E82-4157-AD25-469DD7ABAA69}.Release|Any CPU.Build.0 = Release|Any CPU
{662A0CDC-7E82-4157-AD25-469DD7ABAA69}.Release|x64.ActiveCfg = Release|Any CPU
{662A0CDC-7E82-4157-AD25-469DD7ABAA69}.Release|x64.Build.0 = Release|Any CPU
{662A0CDC-7E82-4157-AD25-469DD7ABAA69}.Release|x86.ActiveCfg = Release|Any CPU
{662A0CDC-7E82-4157-AD25-469DD7ABAA69}.Release|x86.Build.0 = Release|Any CPU
{BCBD73E2-7170-4A85-BFD8-B76F056D4D49}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BCBD73E2-7170-4A85-BFD8-B76F056D4D49}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BCBD73E2-7170-4A85-BFD8-B76F056D4D49}.Debug|x64.ActiveCfg = Debug|Any CPU
{BCBD73E2-7170-4A85-BFD8-B76F056D4D49}.Debug|x64.Build.0 = Debug|Any CPU
{BCBD73E2-7170-4A85-BFD8-B76F056D4D49}.Debug|x86.ActiveCfg = Debug|Any CPU
{BCBD73E2-7170-4A85-BFD8-B76F056D4D49}.Debug|x86.Build.0 = Debug|Any CPU
{BCBD73E2-7170-4A85-BFD8-B76F056D4D49}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BCBD73E2-7170-4A85-BFD8-B76F056D4D49}.Release|Any CPU.Build.0 = Release|Any CPU
{BCBD73E2-7170-4A85-BFD8-B76F056D4D49}.Release|x64.ActiveCfg = Release|Any CPU
{BCBD73E2-7170-4A85-BFD8-B76F056D4D49}.Release|x64.Build.0 = Release|Any CPU
{BCBD73E2-7170-4A85-BFD8-B76F056D4D49}.Release|x86.ActiveCfg = Release|Any CPU
{BCBD73E2-7170-4A85-BFD8-B76F056D4D49}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {79A6998D-BCE6-4EC5-ADBC-69234C0D2EC5}
EndGlobalSection
EndGlobal

16
Oceanbox.FvcomKit.slnx Normal file
View File

@@ -0,0 +1,16 @@
<Solution>
<Configurations>
<Platform Name="Any CPU" />
<Platform Name="x64" />
<Platform Name="x86" />
</Configurations>
<Folder Name="/Solution Items/">
<File Path="LICENSE" />
<File Path="README.md" />
<File Path="shell.nix" />
</Folder>
<Folder Name="/submodules/" />
<Project Path="Build.fsproj" />
<Project Path="src/Oceanbox.FvcomKit.fsproj" />
<Project Path="xtest/xtest.fsproj" />
</Solution>

View File

@@ -1,5 +1,562 @@
# Changelog
# [6.0.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.13.0...v6.0.0) (2026-01-19)
### Bug Fixes
* Update Arome to translate latlon to lambert ([655abeb](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/655abebe526a3587c26429e41130d78f326fa524))
# [5.13.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.12.2...v5.13.0) (2025-11-05)
### Features
* Add ElemsAroundElem to NeighborIndex ([71e861e](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/71e861e4174a57d11cfd85ac04f69c51fc985b4c))
## [5.12.2](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.12.1...v5.12.2) (2025-09-01)
### Bug Fixes
* Bump Oceanbox.SDSLite to 2.8.0 and use bun for SR ([0c55b2d](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/0c55b2dcbdb89337abdbe430a30461d09dbb0bd2))
## [5.12.1](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.12.0...v5.12.1) (2025-05-02)
### Bug Fixes
* include edge point in isInsideTriangle ([c89f35b](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/c89f35bc6e316ac19d2de4ef35dbb78302881373))
* remove unused double precision function ([8715f4d](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/8715f4d8c32d04f6d1c77502435234dadd0863ae))
# [5.12.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.11.0...v5.12.0) (2025-03-07)
### Features
* add Grid.toLonLat function ([06a4aea](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/06a4aeabf1bcd2824db74a1851f0703797fdacf8))
# [5.11.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.10.0...v5.11.0) (2025-03-06)
### Bug Fixes
* init sha once explicitly from sha1 byte[] ([33b7b99](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/33b7b999c8aaff3190dbe8a5e4d3f2e8e1cbf15e))
### Features
* rename grid sha1 to hash for better generality ([bfbaa3a](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/bfbaa3aeff66b39526cf817009729df3fe4966cc))
# [5.10.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.9.1...v5.10.0) (2025-03-06)
### Features
* init sha once explicitly from bingrid ([2e87294](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/2e87294c46579b35096c80b50bc16b89a1352b93))
## [5.9.1](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.9.0...v5.9.1) (2025-03-06)
### Bug Fixes
* use sha from bingrid if it exists ([abdb949](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/abdb94911343c7dff214047ec8bacf740aeffe97))
# [5.9.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.8.0...v5.9.0) (2025-03-06)
### Features
* add ToGrid() method to extended grid ([21e84d0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/21e84d07cd4cf1593ae3e9c9f65718d221adcf1e))
# [5.8.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.7.0...v5.8.0) (2025-03-06)
### Features
* compute sha1 checksum of extended grids ([bf7ae58](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/bf7ae5889b48efb871c8c3b5ee4f07ae6ce34865))
# [5.7.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.6.0...v5.7.0) (2025-02-12)
### Features
* add read omega block ([9abd9d4](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/9abd9d45bc3ce5ec2708f9e2dcbb1366190aad9c))
# [5.6.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.5.5...v5.6.0) (2024-11-27)
### Features
* update to net9.0 ([17685ac](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/17685ac2cd781a05da5064d344ab6e75597d48f9))
## [5.5.5](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.5.4...v5.5.5) (2024-11-27)
### Bug Fixes
* add velocity to nodal function ([ed0ac79](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/ed0ac797d378c7acc0dba29cc5d95f10d1e0fe3a))
## [5.5.4](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.5.3...v5.5.4) (2024-05-01)
### Bug Fixes
* Norshelf filenames changed due to changes in Thredds ([0f5de91](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/0f5de91d39e7fae82b17bfc5fd014f731c7d2b9d))
## [5.5.3](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.5.2...v5.5.3) (2024-02-23)
### Bug Fixes
* bug in isInsideTriangle ([8d7ab41](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/8d7ab4164c2817906c539b1c632c9ea820de7e5d))
## [5.5.2](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.5.1...v5.5.2) (2024-01-04)
### Bug Fixes
* fix ci/cd deploy name ([35e1f43](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/35e1f43fc00dd31c2ec442a3e81d6b715011e66e))
* update ci/cd setup to v2 ([3f61f1d](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/3f61f1d1a9c98dfc6be98540cfbc65e90b50cc36))
## [5.5.1](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.5.0...v5.5.1) (2024-01-03)
### Bug Fixes
* bump to sdk_8 in shell.nix ([bc13065](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/bc130658e97e7811c26aaa132d945c4a0258fa9c))
* update package sdslite.oceanbox-2.7.3 ([aba0917](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/aba0917557b32ad29aabba0d9d59dd8925477a8c))
# [5.5.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.4.1...v5.5.0) (2023-12-31)
### Bug Fixes
* update devcontainer to net8.0 ([d75db35](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/d75db35d8bba9283091447ae08ccf9ec07213f7a))
### Features
* upgrade to net8.0 ([cfbf2b1](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/cfbf2b1d61e1a68c4516ffe36c0ac28526fb88e1))
## [5.4.1](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.4.0...v5.4.1) (2023-10-04)
### Bug Fixes
* fix arome variable names ([0f2b696](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/0f2b69609c5ac8d5cc1e8ddf5e2cdb8e0e2b7ef1))
# [5.4.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.3.0...v5.4.0) (2023-09-25)
### Bug Fixes
* bug in bbox center ([90b50c3](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/90b50c3c8359536ffffb8737cde30682f079fbd3))
* remove depricated grid.projection ([f38f06c](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/f38f06c936d92ca2598f16b56e3d7025d8efb4ae))
* remove print ([d2e362d](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/d2e362df2019f70d75a3d9a79b7f0fee3992e574))
* rescale/translate grid ([5f5ad1b](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/5f5ad1b9e41fbb502db41b9fe327ec970a1a608e))
* update ProjNet (error on unknown proj) ([5479c8b](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/5479c8b598c2c1474f86d841fad1fac8e6b431d5))
### Features
* read tauc from fvcom file ([4ca7fff](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/4ca7fff09ea4eea3a8af66bd71907de96df642c3))
# [5.3.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.2.0...v5.3.0) (2023-09-06)
### Features
* add functtions to read grid in lon-lat format ([902ac08](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/902ac080e9b74167825fbccb086f99f6bad203c2))
# [5.2.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.1.1...v5.2.0) (2023-08-28)
### Bug Fixes
* getTimeInDays returns single, not float ([434ab6d](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/434ab6d5f43390e75f9760bd9310bbbeed678975))
* remove FsKdTree references for faster builds ([86384bf](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/86384bfd9f64e768c93cda2b04c2787897d2b20f))
* update deps ([460f8f1](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/460f8f10eb3aed704c59eaddf587268b43373896))
* update deps ([bb57404](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/bb5740452375984cd97139d64a8a3e5c204f6661))
### Features
* add getTimeInDays to Fvcom accessors ([302c4f1](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/302c4f10488cf017501427e044b6f3669fc28258))
* add getTimsSpanSinceStart function ([764bded](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/764bdedb453c4a8e2bbec3c1cd43ed314fa08e94))
* add Singlular module for time-series (later add Plural) ([eb2a981](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/eb2a98157653d8a7faadbcc7c6106de8befd867a))
* read data in sigma layer blocks ([e7bb554](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/e7bb5540ef74a88efe259ef8063c46ec33af7420))
* read necessary grid prop from file ([ff947bb](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/ff947bb3644af7d09039df5898ce825ad1ca88bf))
* remove projection from grid(s), now in archmesiter ([36bd52d](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/36bd52d3f174ea76f155c680066dca48e9d18a12))
* upgrade to net7.0 ([2be8397](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/2be8397297e586ca7292c40dc65e0484a44483d3))
* working arome querying ([4556a9f](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/4556a9fd9a7f8bd9f15719577336a894e2435ba8))
## [5.1.1](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.1.0...v5.1.1) (2023-02-01)
### Bug Fixes
* compute and not read center variables ([a6a8828](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/a6a8828ae9f403469d827f767cd001f7191370b5))
* compute and not read siglay_center ([a9d7662](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/a9d7662a2b131bb43740d8a279e4162db5a99894))
# [5.1.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.0.3...v5.1.0) (2023-01-16)
### Bug Fixes
* update node in devcontainer ([347a730](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/347a730e135f407d2635a29f63a25796ebf6b2fb))
### Features
* add readOmega for vertical sigma velocity ([7d81ac7](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/7d81ac771a0b14ba5a3da97e4986610056969ff9))
## [5.0.3](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.0.2...v5.0.3) (2023-01-02)
### Bug Fixes
* using S_rho in readVerticalGrid ([886234b](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/886234b1659a214755b8e439e99d003033f992fd))
## [5.0.2](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.0.1...v5.0.2) (2023-01-02)
### Bug Fixes
* use S_rho as vertical roms coordinate ([f851e54](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/f851e5411b5c248385be216de1409fda68603a7b))
## [5.0.1](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v5.0.0...v5.0.1) (2022-12-19)
### Bug Fixes
* update ProjNet.FSharp ([0bf255d](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/0bf255d51056bfe506d3c9da7f2765d74aa75244))
# [5.0.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v4.6.0...v5.0.0) (2022-12-07)
### Bug Fixes
* update ProjNet.FSharp ([ab63c39](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/ab63c39993906dbdf80f9fd19ec636981a348d0f))
# [4.6.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v4.5.3...v4.6.0) (2022-12-02)
### Features
* add tryGetNode grid method ([8c1c3e7](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/8c1c3e750f67d3a4c50b0a3bf1f74a8bfa792410))
## [4.5.3](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v4.5.2...v4.5.3) (2022-12-02)
### Bug Fixes
* make siglev/lay readers faster ([a2b6a3b](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/a2b6a3b04fa71b38beffa7c06fcdc5ae076b3299))
## [4.5.2](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v4.5.1...v4.5.2) (2022-11-16)
### Bug Fixes
* fix obc indexing off by one ([0bf1889](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/0bf18891b26631c2a1065cf49415e59a96efe4fa))
## [4.5.1](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v4.5.0...v4.5.1) (2022-11-15)
### Bug Fixes
* update ProjNet.FSharp ([1939213](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/1939213ec40f0e3ca46676e97d3e9a05a42d92d1))
# [4.5.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v4.4.1...v4.5.0) (2022-11-03)
### Features
* add functions to ger number of sigmas ([7c977ab](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/7c977abaa3b7a36fade925db3aaa99122ffdb482))
## [4.4.1](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v4.4.0...v4.4.1) (2022-10-18)
### Bug Fixes
* store Fvcom field as single, but use as float ([8cd202a](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/8cd202a6993a7d59a138d4ccefe8251bf47dc0b2))
# [4.4.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v4.3.0...v4.4.0) (2022-10-17)
### Features
* add readUV(W) ranges ([e2c977b](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/e2c977b843552f573cb672b60c2aa78ae2db8732))
# [4.3.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v4.2.0...v4.3.0) (2022-10-12)
### Features
* compute circumscribed circle around element ([3c9a7a7](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/3c9a7a701ef1cdc3d168608f4d85dc2b08a8de6e))
# [4.2.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v4.1.0...v4.2.0) (2022-10-05)
### Features
* add Fvcom.getTime ([cbe31b9](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/cbe31b94b50a6cf7a066e095af27ff073855c119))
# [4.1.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v4.0.0...v4.1.0) (2022-09-30)
### Features
* project on boundary ([0a8b31c](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/0a8b31c1f3b85ccdf9b5e108499352aabe7e0e79))
# [4.0.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.14.0...v4.0.0) (2022-09-17)
### Bug Fixes
* reformat src with fantoms ([7955a76](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/7955a76178cbe890901cf825bf9fc06013cf56ee))
# [3.14.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.13.0...v3.14.0) (2022-09-12)
### Features
* add readUVW(s) and fix bug in readUVs ([80b2651](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/80b2651f2441b8b85037995a400c38db581c18bd))
# [3.13.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.12.2...v3.13.0) (2022-09-09)
### Features
* add elemental accessors for U/V and WW ([060c775](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/060c7753a2f04cf6388a9bec62e92fe9759de6e2))
## [3.12.2](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.12.1...v3.12.2) (2022-09-09)
### Bug Fixes
* update evaluateLinearCentroid to take a readUV fvcom function ([aab00e9](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/aab00e965134814e29893ffad90246b773108a3a))
## [3.12.1](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.12.0...v3.12.1) (2022-09-09)
### Bug Fixes
* extract node element tree from option before saving to disk ([c6175f2](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/c6175f2e140e5a9dcbee16adace86a03a0e97db2))
# [3.12.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.11.5...v3.12.0) (2022-09-09)
### Features
* Clough-Tocher interpolation ([8838018](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/8838018ee08f32ba758fbf5662bde1e39daf7455))
## [3.11.5](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.11.4...v3.11.5) (2022-09-07)
### Bug Fixes
* don't use dotnet restore lock file ([b4500ce](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/b4500ceaa6d173540515af902ef91ee5f5ea0b7d))
* revert to C# kd-tree for now ([c857856](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/c857856b5eed122bf36ea5363bedce3bb9907eae))
## [3.11.4](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.11.3...v3.11.4) (2022-09-06)
### Bug Fixes
* update vendor dependencies ([5c84402](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/5c844026b61f589626e49e8edcf8d7dd56c2d7dc))
## [3.11.3](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.11.2...v3.11.3) (2022-09-06)
### Bug Fixes
* reenable FsKdTree (test) ([856fdbe](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/856fdbee40c6fc9961d31c92239e0cc2ff025302))
* update ProjNet.FSharp ([8e93280](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/8e9328098b545c264e989fd0589b0b0b45147bad))
* update ProjNet.FSharp ([3345ce6](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/3345ce6fbd3ad5bf4bd0a8b80290b059bc76be0b))
## [3.11.2](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.11.1...v3.11.2) (2022-09-06)
### Bug Fixes
* submodules use https ([8782ca4](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/8782ca450c84b72d170240c3929697ec09cc6a11))
* submodules use relpath ([135def2](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/135def257336183b59c1bfe5f8b1448e722b8c0a))
* use local vendor folder with git submodules ([7c22c3b](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/7c22c3bdc3a6a5e245dd4b3fddb5f4b87d2d159e))
## [3.11.1](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.11.0...v3.11.1) (2022-09-06)
### Bug Fixes
* remove Sdk.Web dependency ([9fa5a37](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/9fa5a37709e47d076bd73a3a80114993016612ee))
# [3.11.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.10.4...v3.11.0) (2022-08-29)
### Features
* evaluate 2D fields in arbitrary points ([3d63732](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/3d637324501df917adbe55845e4b5c295cd3a4d9))
## [3.10.4](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.10.3...v3.10.4) (2022-08-29)
### Bug Fixes
* update deps ([9382b46](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/9382b46f38ce93c97e44a02b0451eabeaf641205))
## [3.10.3](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.10.2...v3.10.3) (2022-08-20)
### Bug Fixes
* do not calculate centroids on node tree ([7e41169](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/7e411693d588bffa6021e1439c7d76d23acfba35))
* do not save index tree as option ([8ba4de5](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/8ba4de5bb08e547d2935abd4d123958b8380d0fa))
## [3.10.2](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.10.1...v3.10.2) (2022-07-14)
### Bug Fixes
* bug in makeNestTree ([af26509](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/af26509fa415af19bb52c18f5858f5b687c4646c))
## [3.10.1](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.10.0...v3.10.1) (2022-07-08)
### Bug Fixes
* Make tryFindElementTwice private not to confuse users ([75aa453](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/75aa45392f9771a57edc71129457678985718b2b))
# [3.10.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.9.0...v3.10.0) (2022-07-07)
### Features
* add methods to pickle and unpickle neighbor index ([252141f](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/252141fea03c5028698e3efee8c4fdcb723d02fd))
# [3.9.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.8.3...v3.9.0) (2022-07-07)
### Features
* switch to FsKDTree ([0ae2652](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/0ae2652fcc7e01005d74e15823a2ea4a58e06dbd))
## [3.8.3](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.8.2...v3.8.3) (2022-07-06)
## [3.8.2](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.8.1...v3.8.2) (2022-06-23)
### Bug Fixes
* remove debug prints ([a5698ff](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/a5698ff89a2979a22f95e81989351c5a4414937d))
## [3.8.1](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.8.0...v3.8.1) (2022-06-22)
### Bug Fixes
* fix insideTriangle (take 2) ([7efc6a1](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/7efc6a1d528086ec5007c5b286561f83659df738))
# [3.8.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.7.0...v3.8.0) (2022-06-20)
### Bug Fixes
* fix insideTriangle ([ea29fcb](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/ea29fcb6013d9516e792a349d15828b3796ae9c0))
### Features
* add getNumFrames per Fvcom dataset archive ([ff268d4](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/ff268d461fcf60ee08a694054e4a8e20c590a0ac))
# [3.7.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.6.0...v3.7.0) (2022-06-17)
### Features
* ExtendedGrid and refactor grids and helpers ([8143571](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/8143571a4b45d296acf7471f38ca635bf53d6fd0))
# [3.6.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.5.0...v3.6.0) (2022-06-16)
### Features
* add helpers to efficiently find nearest element and more ([225754b](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/225754bc4b3f743ea4278a7c49857cba2f87924e))
# [3.5.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.4.0...v3.5.0) (2022-06-15)
### Bug Fixes
* track upstream ProjNet.FSharp changes ([1af2f32](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/1af2f322dd92c3486066231ae63765017eabb127))
### Features
* add coordinate projection to grids ([7012d2b](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/7012d2b2caed3686feebba138f13b8891444b846))
# [3.4.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.3.0...v3.4.0) (2022-06-15)
### Features
* add routines to convert elemental to nodal props ([1c74935](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/1c749355fbb0eb0a28bc09f3326d2acaef9fb926))
# [3.3.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.2.0...v3.3.0) (2022-06-09)
### Bug Fixes
* bug fix zeta getter ([b564dec](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/b564dec305c09d8214b09aa8f7adfa97826ffeeb))
### Features
* add getter for zeta ([db7fe87](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/db7fe87a5a1d21bac1da817b0d88722aa8f9d71f))
# [3.2.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.1.0...v3.2.0) (2022-05-17)
### Bug Fixes
* refactor for better code structure ([b37d257](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/b37d257674750f434ea482d68f5001923ed11c14))
### Features
* bilinear interpolation ([37613cb](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/37613cb19fb4c00bf7cd266507c8a0adc00a9645))
# [3.1.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.0.3...v3.1.0) (2022-05-11)
### Features
* Remove functions really belonging to Primus ([b43466f](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/b43466fb24a499d969970353822e6b08d52bca60))
## [3.0.3](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.0.2...v3.0.3) (2022-05-11)
### Bug Fixes
* remove writeFvcomRestart ([f869d26](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/f869d2611b007ef5fad658504103ca2f235fc6ab))
## [3.0.2](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.0.1...v3.0.2) (2022-05-11)
### Bug Fixes
* add warning for empty kd-trees (input error) ([ecceccd](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/ecceccd29784140737be19f73e910dc9cdf4b882))
## [3.0.1](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v3.0.0...v3.0.1) (2022-05-11)
### Bug Fixes
* generalize Grid projections ([7ebad27](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/7ebad274193469e15fad04e2e9a4ecb4102dc035))
* update deps ([aef93d2](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/commit/aef93d2d4fa81fdee8fb8676b673bb984f964132))
# [3.0.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v2.1.0...v3.0.0) (2022-05-11)
# [2.1.0](https://gitlab.com/oceanbox/Oceanbox.FvcomKit/compare/v2.0.1...v2.1.0) (2022-05-10)

302
bun.lock Normal file
View File

@@ -0,0 +1,302 @@
{
"lockfileVersion": 1,
"configVersion": 0,
"workspaces": {
"": {
"devDependencies": {
"@semantic-release/changelog": "^6.0.1",
"@semantic-release/exec": "^6.0.3",
"@semantic-release/git": "^10.0.1",
"@semantic-release/gitlab": "^7.0.4",
"semantic-release-dotnet": "^1.0.0",
},
},
},
"packages": {
"@babel/code-frame": ["@babel/code-frame@7.16.7", "", { "dependencies": { "@babel/highlight": "^7.16.7" } }, "sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg=="],
"@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.16.7", "", {}, "sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw=="],
"@babel/highlight": ["@babel/highlight@7.16.7", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.16.7", "chalk": "^2.0.0", "js-tokens": "^4.0.0" } }, "sha512-aKpPMfLvGO3Q97V0qhw/V2SWNWlwfJknuwAunU7wZLSfrM4xTBvg7E5opUVi1kJTBKihE38CPg4nBiqX83PWYw=="],
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
"@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
"@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
"@semantic-release/changelog": ["@semantic-release/changelog@6.0.1", "", { "dependencies": { "@semantic-release/error": "^3.0.0", "aggregate-error": "^3.0.0", "fs-extra": "^9.0.0", "lodash": "^4.17.4" } }, "sha512-FT+tAGdWHr0RCM3EpWegWnvXJ05LQtBkQUaQRIExONoXjVjLuOILNm4DEKNaV+GAQyJjbLRVs57ti//GypH6PA=="],
"@semantic-release/error": ["@semantic-release/error@3.0.0", "", {}, "sha512-5hiM4Un+tpl4cKw3lV4UgzJj+SmfNIDCLLw0TepzQxz9ZGV5ixnqkzIVF+3tp0ZHgcMKE+VNGHJjEeyFG2dcSw=="],
"@semantic-release/exec": ["@semantic-release/exec@6.0.3", "", { "dependencies": { "@semantic-release/error": "^3.0.0", "aggregate-error": "^3.0.0", "debug": "^4.0.0", "execa": "^5.0.0", "lodash": "^4.17.4", "parse-json": "^5.0.0" } }, "sha512-bxAq8vLOw76aV89vxxICecEa8jfaWwYITw6X74zzlO0mc/Bgieqx9kBRz9z96pHectiTAtsCwsQcUyLYWnp3VQ=="],
"@semantic-release/git": ["@semantic-release/git@10.0.1", "", { "dependencies": { "@semantic-release/error": "^3.0.0", "aggregate-error": "^3.0.0", "debug": "^4.0.0", "dir-glob": "^3.0.0", "execa": "^5.0.0", "lodash": "^4.17.4", "micromatch": "^4.0.0", "p-reduce": "^2.0.0" } }, "sha512-eWrx5KguUcU2wUPaO6sfvZI0wPafUKAMNC18aXY4EnNcrZL86dEmpNVnC9uMpGZkmZJ9EfCVJBQx4pV4EMGT1w=="],
"@semantic-release/gitlab": ["@semantic-release/gitlab@7.0.4", "", { "dependencies": { "@semantic-release/error": "^3.0.0", "aggregate-error": "^3.0.0", "debug": "^4.0.0", "dir-glob": "^3.0.0", "escape-string-regexp": "^3.0.0", "form-data": "^4.0.0", "fs-extra": "^10.0.0", "globby": "^11.0.0", "got": "^11.0.0", "lodash": "^4.17.11", "parse-path": "^4.0.0", "url-join": "^4.0.0" } }, "sha512-TL6kT526+ir/uehMFdTlJNXUj+p+SjPAYUkit6lh5Rs8kxeHQ01bgmpYLQlc94ZDpy9x2Tzcb/NRwKojkmLG4A=="],
"@sindresorhus/is": ["@sindresorhus/is@4.3.0", "", {}, "sha512-wwOvh0eO3PiTEivGJWiZ+b946SlMSb4pe+y+Ur/4S87cwo09pYi+FWHHnbrM3W9W7cBYKDqQXcrFYjYUCOJUEQ=="],
"@szmarczak/http-timer": ["@szmarczak/http-timer@4.0.6", "", { "dependencies": { "defer-to-connect": "^2.0.0" } }, "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w=="],
"@types/cacheable-request": ["@types/cacheable-request@6.0.2", "", { "dependencies": { "@types/http-cache-semantics": "*", "@types/keyv": "*", "@types/node": "*", "@types/responselike": "*" } }, "sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA=="],
"@types/glob": ["@types/glob@7.2.0", "", { "dependencies": { "@types/minimatch": "*", "@types/node": "*" } }, "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA=="],
"@types/http-cache-semantics": ["@types/http-cache-semantics@4.0.1", "", {}, "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ=="],
"@types/keyv": ["@types/keyv@3.1.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-FXCJgyyN3ivVgRoml4h94G/p3kY+u/B86La+QptcqJaWtBWtmc6TtkNfS40n9bIvyLteHh7zXOtgbobORKPbDg=="],
"@types/minimatch": ["@types/minimatch@3.0.5", "", {}, "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ=="],
"@types/node": ["@types/node@17.0.9", "", {}, "sha512-5dNBXu/FOER+EXnyah7rn8xlNrfMOQb/qXnw4NQgLkCygKBKhdmF/CA5oXVOKZLBEahw8s2WP9LxIcN/oDDRgQ=="],
"@types/responselike": ["@types/responselike@1.0.0", "", { "dependencies": { "@types/node": "*" } }, "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA=="],
"aggregate-error": ["aggregate-error@3.1.0", "", { "dependencies": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" } }, "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA=="],
"ansi-styles": ["ansi-styles@3.2.1", "", { "dependencies": { "color-convert": "^1.9.0" } }, "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA=="],
"array-union": ["array-union@2.1.0", "", {}, "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw=="],
"asynckit": ["asynckit@0.4.0", "", {}, "sha1-x57Zf380y48robyXkLzDZkdLS3k="],
"at-least-node": ["at-least-node@1.0.0", "", {}, "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg=="],
"balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
"brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="],
"braces": ["braces@3.0.2", "", { "dependencies": { "fill-range": "^7.0.1" } }, "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A=="],
"cacheable-lookup": ["cacheable-lookup@5.0.4", "", {}, "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA=="],
"cacheable-request": ["cacheable-request@7.0.2", "", { "dependencies": { "clone-response": "^1.0.2", "get-stream": "^5.1.0", "http-cache-semantics": "^4.0.0", "keyv": "^4.0.0", "lowercase-keys": "^2.0.0", "normalize-url": "^6.0.1", "responselike": "^2.0.0" } }, "sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew=="],
"call-bind": ["call-bind@1.0.2", "", { "dependencies": { "function-bind": "^1.1.1", "get-intrinsic": "^1.0.2" } }, "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA=="],
"chalk": ["chalk@2.4.2", "", { "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" } }, "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ=="],
"clean-stack": ["clean-stack@2.2.0", "", {}, "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A=="],
"clone-response": ["clone-response@1.0.2", "", { "dependencies": { "mimic-response": "^1.0.0" } }, "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws="],
"color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="],
"color-name": ["color-name@1.1.3", "", {}, "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU="],
"combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="],
"concat-map": ["concat-map@0.0.1", "", {}, "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="],
"cross-spawn": ["cross-spawn@7.0.3", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w=="],
"debug": ["debug@4.3.3", "", { "dependencies": { "ms": "2.1.2" } }, "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q=="],
"decode-uri-component": ["decode-uri-component@0.2.0", "", {}, "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU="],
"decompress-response": ["decompress-response@6.0.0", "", { "dependencies": { "mimic-response": "^3.1.0" } }, "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ=="],
"defer-to-connect": ["defer-to-connect@2.0.1", "", {}, "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg=="],
"delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="],
"dir-glob": ["dir-glob@3.0.1", "", { "dependencies": { "path-type": "^4.0.0" } }, "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA=="],
"end-of-stream": ["end-of-stream@1.4.4", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q=="],
"error-ex": ["error-ex@1.3.2", "", { "dependencies": { "is-arrayish": "^0.2.1" } }, "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g=="],
"escape-string-regexp": ["escape-string-regexp@3.0.0", "", {}, "sha512-11dXIUC3umvzEViLP117d0KN6LJzZxh5+9F4E/7WLAAw7GrHk8NpUR+g9iJi/pe9C0py4F8rs0hreyRCwlAuZg=="],
"execa": ["execa@5.1.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg=="],
"fast-glob": ["fast-glob@3.2.11", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.4" } }, "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew=="],
"fastq": ["fastq@1.13.0", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw=="],
"fill-range": ["fill-range@7.0.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ=="],
"filter-obj": ["filter-obj@1.1.0", "", {}, "sha1-mzERErxsYSehbgFsbF1/GeCAXFs="],
"form-data": ["form-data@4.0.0", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "mime-types": "^2.1.12" } }, "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww=="],
"fs-extra": ["fs-extra@9.1.0", "", { "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ=="],
"fs.realpath": ["fs.realpath@1.0.0", "", {}, "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="],
"function-bind": ["function-bind@1.1.1", "", {}, "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="],
"get-intrinsic": ["get-intrinsic@1.1.1", "", { "dependencies": { "function-bind": "^1.1.1", "has": "^1.0.3", "has-symbols": "^1.0.1" } }, "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q=="],
"get-stream": ["get-stream@6.0.1", "", {}, "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg=="],
"glob": ["glob@7.2.0", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.0.4", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q=="],
"glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
"glob-promise": ["glob-promise@4.2.2", "", { "dependencies": { "@types/glob": "^7.1.3" }, "peerDependencies": { "glob": "^7.1.6" } }, "sha512-xcUzJ8NWN5bktoTIX7eOclO1Npxd/dyVqUJxlLIDasT4C7KZyqlPIwkdJ0Ypiy3p2ZKahTjK4M9uC3sNSfNMzw=="],
"globby": ["globby@11.1.0", "", { "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", "fast-glob": "^3.2.9", "ignore": "^5.2.0", "merge2": "^1.4.1", "slash": "^3.0.0" } }, "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g=="],
"got": ["got@11.8.3", "", { "dependencies": { "@sindresorhus/is": "^4.0.0", "@szmarczak/http-timer": "^4.0.5", "@types/cacheable-request": "^6.0.1", "@types/responselike": "^1.0.0", "cacheable-lookup": "^5.0.3", "cacheable-request": "^7.0.2", "decompress-response": "^6.0.0", "http2-wrapper": "^1.0.0-beta.5.2", "lowercase-keys": "^2.0.0", "p-cancelable": "^2.0.0", "responselike": "^2.0.0" } }, "sha512-7gtQ5KiPh1RtGS9/Jbv1ofDpBFuq42gyfEib+ejaRBJuj/3tQFeR5+gw57e4ipaU8c/rCjvX6fkQz2lyDlGAOg=="],
"graceful-fs": ["graceful-fs@4.2.9", "", {}, "sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ=="],
"has": ["has@1.0.3", "", { "dependencies": { "function-bind": "^1.1.1" } }, "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw=="],
"has-flag": ["has-flag@3.0.0", "", {}, "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="],
"has-symbols": ["has-symbols@1.0.2", "", {}, "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw=="],
"http-cache-semantics": ["http-cache-semantics@4.1.0", "", {}, "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ=="],
"http2-wrapper": ["http2-wrapper@1.0.3", "", { "dependencies": { "quick-lru": "^5.1.1", "resolve-alpn": "^1.0.0" } }, "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg=="],
"human-signals": ["human-signals@2.1.0", "", {}, "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="],
"ignore": ["ignore@5.2.0", "", {}, "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ=="],
"indent-string": ["indent-string@4.0.0", "", {}, "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg=="],
"inflight": ["inflight@1.0.6", "", { "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk="],
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
"is-arrayish": ["is-arrayish@0.2.1", "", {}, "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0="],
"is-extglob": ["is-extglob@2.1.1", "", {}, "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI="],
"is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
"is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="],
"is-ssh": ["is-ssh@1.3.3", "", { "dependencies": { "protocols": "^1.1.0" } }, "sha512-NKzJmQzJfEEma3w5cJNcUMxoXfDjz0Zj0eyCalHn2E6VOwlzjZo0yuO2fcBSf8zhFuVCL/82/r5gRcoi6aEPVQ=="],
"is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="],
"isexe": ["isexe@2.0.0", "", {}, "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="],
"js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
"json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="],
"json-parse-even-better-errors": ["json-parse-even-better-errors@2.3.1", "", {}, "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="],
"jsonfile": ["jsonfile@6.1.0", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ=="],
"keyv": ["keyv@4.0.5", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-531pkGLqV3BMg0eDqqJFI0R1mkK1Nm5xIP2mM6keP5P8WfFtCkg2IOwplTUmlGoTgIg9yQYZ/kdihhz89XH3vA=="],
"lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="],
"lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="],
"lowercase-keys": ["lowercase-keys@2.0.0", "", {}, "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA=="],
"merge-stream": ["merge-stream@2.0.0", "", {}, "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="],
"merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="],
"micromatch": ["micromatch@4.0.4", "", { "dependencies": { "braces": "^3.0.1", "picomatch": "^2.2.3" } }, "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg=="],
"mime-db": ["mime-db@1.51.0", "", {}, "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g=="],
"mime-types": ["mime-types@2.1.34", "", { "dependencies": { "mime-db": "1.51.0" } }, "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A=="],
"mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="],
"mimic-response": ["mimic-response@3.1.0", "", {}, "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="],
"minimatch": ["minimatch@3.0.4", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA=="],
"ms": ["ms@2.1.2", "", {}, "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="],
"normalize-url": ["normalize-url@6.1.0", "", {}, "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A=="],
"npm-run-path": ["npm-run-path@4.0.1", "", { "dependencies": { "path-key": "^3.0.0" } }, "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw=="],
"object-inspect": ["object-inspect@1.12.0", "", {}, "sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g=="],
"once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha1-WDsap3WWHUsROsF9nFC6753Xa9E="],
"onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="],
"p-cancelable": ["p-cancelable@2.1.1", "", {}, "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg=="],
"p-reduce": ["p-reduce@2.1.0", "", {}, "sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw=="],
"parse-json": ["parse-json@5.2.0", "", { "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" } }, "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg=="],
"parse-path": ["parse-path@4.0.3", "", { "dependencies": { "is-ssh": "^1.3.0", "protocols": "^1.4.0", "qs": "^6.9.4", "query-string": "^6.13.8" } }, "sha512-9Cepbp2asKnWTJ9x2kpw6Fe8y9JDbqwahGCTvklzd/cEq5C5JC59x2Xb0Kx+x0QZ8bvNquGO8/BWP0cwBHzSAA=="],
"path-is-absolute": ["path-is-absolute@1.0.1", "", {}, "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="],
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
"path-type": ["path-type@4.0.0", "", {}, "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw=="],
"picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="],
"protocols": ["protocols@1.4.8", "", {}, "sha512-IgjKyaUSjsROSO8/D49Ab7hP8mJgTYcqApOqdPhLoPxAplXmkp+zRvsrSQjFn5by0rhm4VH0GAUELIPpx7B1yg=="],
"pump": ["pump@3.0.0", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww=="],
"qs": ["qs@6.10.3", "", { "dependencies": { "side-channel": "^1.0.4" } }, "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ=="],
"query-string": ["query-string@6.14.1", "", { "dependencies": { "decode-uri-component": "^0.2.0", "filter-obj": "^1.1.0", "split-on-first": "^1.0.0", "strict-uri-encode": "^2.0.0" } }, "sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw=="],
"queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
"quick-lru": ["quick-lru@5.1.1", "", {}, "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA=="],
"resolve-alpn": ["resolve-alpn@1.2.1", "", {}, "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g=="],
"responselike": ["responselike@2.0.0", "", { "dependencies": { "lowercase-keys": "^2.0.0" } }, "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw=="],
"reusify": ["reusify@1.0.4", "", {}, "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw=="],
"run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="],
"sax": ["sax@1.2.4", "", {}, "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="],
"semantic-release-dotnet": ["semantic-release-dotnet@1.0.0", "", { "dependencies": { "glob": "^7.1.7", "glob-promise": "^4.2.0", "xml-js": "^1.6.11" } }, "sha512-U/cHwqqzFbJpPCQ/KMTSZtwzPNWCNCVStZRznMGK0xjhiLoDfRe5KFRs/9dzWBtPa358D7IiVzz97ZzepUAtfQ=="],
"shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
"side-channel": ["side-channel@1.0.4", "", { "dependencies": { "call-bind": "^1.0.0", "get-intrinsic": "^1.0.2", "object-inspect": "^1.9.0" } }, "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw=="],
"signal-exit": ["signal-exit@3.0.6", "", {}, "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ=="],
"slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="],
"split-on-first": ["split-on-first@1.1.0", "", {}, "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw=="],
"strict-uri-encode": ["strict-uri-encode@2.0.0", "", {}, "sha1-ucczDHBChi9rFC3CdLvMWGbONUY="],
"strip-final-newline": ["strip-final-newline@2.0.0", "", {}, "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA=="],
"supports-color": ["supports-color@5.5.0", "", { "dependencies": { "has-flag": "^3.0.0" } }, "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow=="],
"to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
"universalify": ["universalify@2.0.0", "", {}, "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ=="],
"url-join": ["url-join@4.0.1", "", {}, "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA=="],
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
"wrappy": ["wrappy@1.0.2", "", {}, "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="],
"xml-js": ["xml-js@1.6.11", "", { "dependencies": { "sax": "^1.2.4" }, "bin": "bin/cli.js" }, "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g=="],
"@semantic-release/gitlab/fs-extra": ["fs-extra@10.0.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ=="],
"cacheable-request/get-stream": ["get-stream@5.2.0", "", { "dependencies": { "pump": "^3.0.0" } }, "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA=="],
"chalk/escape-string-regexp": ["escape-string-regexp@1.0.5", "", {}, "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="],
"clone-response/mimic-response": ["mimic-response@1.0.1", "", {}, "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ=="],
}
}

19
default.nix Normal file
View File

@@ -0,0 +1,19 @@
{
sources ? import ./nix,
system ? builtins.currentSystem,
pkgs ? import sources.nixpkgs {
inherit system;
config = { };
overlays = [ ];
},
}:
let
sdk = pkgs.dotnetCorePackages.sdk_9_0;
sdslite = pkgs.callPackage ./nix/sdslite.nix { dotnet-sdk = sdk; };
projnetFsharp = pkgs.callPackage ./nix/projnet.fsharp.nix { dotnet-sdk = sdk; };
in
pkgs.callPackage ./src {
SDSLite = sdslite;
projnet = projnetFsharp;
dotnet-sdk = sdk;
}

146
nix/default.nix Normal file
View File

@@ -0,0 +1,146 @@
/*
This file is provided under the MIT licence:
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the Software), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
# Generated by npins. Do not modify; will be overwritten regularly
let
data = builtins.fromJSON (builtins.readFile ./sources.json);
version = data.version;
# https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/lists.nix#L295
range =
first: last: if first > last then [ ] else builtins.genList (n: first + n) (last - first + 1);
# https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L257
stringToCharacters = s: map (p: builtins.substring p 1 s) (range 0 (builtins.stringLength s - 1));
# https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L269
stringAsChars = f: s: concatStrings (map f (stringToCharacters s));
concatMapStrings = f: list: concatStrings (map f list);
concatStrings = builtins.concatStringsSep "";
# If the environment variable NPINS_OVERRIDE_${name} is set, then use
# the path directly as opposed to the fetched source.
# (Taken from Niv for compatibility)
mayOverride =
name: path:
let
envVarName = "NPINS_OVERRIDE_${saneName}";
saneName = stringAsChars (c: if (builtins.match "[a-zA-Z0-9]" c) == null then "_" else c) name;
ersatz = builtins.getEnv envVarName;
in
if ersatz == "" then
path
else
# this turns the string into an actual Nix path (for both absolute and
# relative paths)
builtins.trace "Overriding path of \"${name}\" with \"${ersatz}\" due to set \"${envVarName}\"" (
if builtins.substring 0 1 ersatz == "/" then
/. + ersatz
else
/. + builtins.getEnv "PWD" + "/${ersatz}"
);
mkSource =
name: spec:
assert spec ? type;
let
path =
if spec.type == "Git" then
mkGitSource spec
else if spec.type == "GitRelease" then
mkGitSource spec
else if spec.type == "PyPi" then
mkPyPiSource spec
else if spec.type == "Channel" then
mkChannelSource spec
else if spec.type == "Tarball" then
mkTarballSource spec
else
builtins.throw "Unknown source type ${spec.type}";
in
spec // { outPath = mayOverride name path; };
mkGitSource =
{
repository,
revision,
url ? null,
submodules,
hash,
branch ? null,
...
}:
assert repository ? type;
# At the moment, either it is a plain git repository (which has an url), or it is a GitHub/GitLab repository
# In the latter case, there we will always be an url to the tarball
if url != null && !submodules then
builtins.fetchTarball {
inherit url;
sha256 = hash; # FIXME: check nix version & use SRI hashes
}
else
let
url =
if repository.type == "Git" then
repository.url
else if repository.type == "GitHub" then
"https://github.com/${repository.owner}/${repository.repo}.git"
else if repository.type == "GitLab" then
"${repository.server}/${repository.repo_path}.git"
else
throw "Unrecognized repository type ${repository.type}";
urlToName =
url: rev:
let
matched = builtins.match "^.*/([^/]*)(\\.git)?$" url;
short = builtins.substring 0 7 rev;
appendShort = if (builtins.match "[a-f0-9]*" rev) != null then "-${short}" else "";
in
"${if matched == null then "source" else builtins.head matched}${appendShort}";
name = urlToName url revision;
in
builtins.fetchGit {
rev = revision;
inherit name;
# hash = hash;
inherit url submodules;
};
mkPyPiSource =
{ url, hash, ... }:
builtins.fetchurl {
inherit url;
sha256 = hash;
};
mkChannelSource =
{ url, hash, ... }:
builtins.fetchTarball {
inherit url;
sha256 = hash;
};
mkTarballSource =
{
url,
locked_url ? url,
hash,
...
}:
builtins.fetchTarball {
url = locked_url;
sha256 = hash;
};
in
if version == 5 then
builtins.mapAttrs mkSource data.pins
else
throw "Unsupported format version ${toString version} in sources.json. Try running `npins upgrade`"

29
nix/projnet.fsharp.nix Normal file
View File

@@ -0,0 +1,29 @@
{
dotnet-sdk,
fetchFromGitLab,
buildDotnetModule
}:
let
src = fetchFromGitLab {
owner = "oceanbox";
repo = "ProjNet.FSharp";
# tag = "v5.2.0";
rev = "722ce0c23fda6844a81e995afbb2d81cbd5f38ec";
private = true;
forceFetchGit = true;
hash = "sha256-Rvnnf/D2x90pwgvTbXz307MJVBlVPK/cCf1hqj2VosE=";
};
in
buildDotnetModule {
name = "ProjNet.FSharp";
src = src;
dotnet-sdk = dotnet-sdk;
projectFile = "src/ProjNet.FSharp.fsproj";
nugetDeps = "${src}/src/deps.json";
packNupkg = true;
executables = [ ];
}

29
nix/sdslite.nix Normal file
View File

@@ -0,0 +1,29 @@
{
dotnet-sdk,
fetchFromGitLab,
buildDotnetModule
}:
let
src = fetchFromGitLab {
owner = "oceanbox";
repo = "SDSlite";
# tag = "v2.8.0";
rev = "8c1a158206c37bc57a5bd726a792bd6a9cd2ec01";
private = true;
forceFetchGit = true;
hash = "sha256-i9pNrmH/VC0Q9FCldbWGdZHkqSL1cdYtAOs7vX+DlXM=";
};
in
buildDotnetModule {
name = "Oceanbox.SDSLite";
src = src;
dotnet-sdk = dotnet-sdk;
projectFile = "ScientificDataSet/ScientificDataSet.csproj";
nugetDeps = "${src}/ScientificDataSet/deps.json";
packNupkg = true;
executables = [ ];
}

23
nix/sources.json Normal file
View File

@@ -0,0 +1,23 @@
{
"pins": {
"nix-utils": {
"type": "Git",
"repository": {
"type": "Git",
"url": "https://git.sr.ht/~mrtz/nix-utils"
},
"branch": "trunk",
"submodules": false,
"revision": "098f594425d2b9dde0657becad0f6498d074f8b3",
"url": null,
"hash": "0hh52w1fkpr1xx6j8cjm6g88j2352yv2ysqm1q51j59y6f583vyb"
},
"nixpkgs": {
"type": "Channel",
"name": "nixpkgs-unstable",
"url": "https://releases.nixos.org/nixpkgs/nixpkgs-26.05pre905319.f720de590661/nixexprs.tar.xz",
"hash": "07n4hhch0j6n69b0zchdjg0l80z2xrdk7k57ykv90cvhklim5dz1"
}
},
"version": 5
}

4
nix/sources.nix Normal file
View File

@@ -0,0 +1,4 @@
{
"ProjNet.FSharp" = "https://gitlab.com/api/v4/projects/35009572/packages/nuget/download";
"Oceanbox.SDSLite" = "https://gitlab.com/api/v4/projects/34025102/packages/nuget/download";
}

10009
package-lock.json generated

File diff suppressed because it is too large Load Diff

25
shell.nix Normal file
View File

@@ -0,0 +1,25 @@
let
sources = import ./nix;
pkgs = import sources.nixpkgs {};
dotnet-sdk = pkgs.dotnetCorePackages.sdk_9_0;
in
with import <nixpkgs> { };
mkShell rec {
packages = [
bun
dotnet-sdk
fsautocomplete
fantomas
npins
nixfmt
nuget-to-json
];
buildInputs = [
netcdf
stdenv.cc.cc.lib
];
DOTNET_ROOT = "${dotnet-sdk}/share/dotnet";
LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath buildInputs;
}

309
src/Adjoin.fs Normal file
View File

@@ -0,0 +1,309 @@
module Oceanbox.FvcomKit.Adjoin
open FSharpPlus
//open FsKDTree
open ProjNet.FSharp
open Grid
open Types
open Polygon
open KdTree // C# version
type CropBox = (float * float) * (float * float)
type Mask = bool[,]
type Pos = float * float
type PosVec = Pos[]
type BiPos = float[,] * float[,]
let private reProject (proj: IProj) ((lng, lat): BiPos) =
lng
|> Array2D.mapi (fun i j lon -> lon, lat[i, j])
|> Array2D.map proj.project
|> unzip2D
let private genCropIdx ((min, max): float * float) (m: float[,]) =
[|
for i = 0 to Array2D.length1 m - 1 do
for j = 0 to Array2D.length2 m - 1 do
if m[i, j] >= min && m[i, j] <= max then i, j else ()
|]
|> Set.ofArray
let private genCropMask ((xlim, ylim): CropBox) ((x, y): BiPos) =
let ind1 = genCropIdx xlim x
let ind2 = genCropIdx ylim y
Set.intersect ind1 ind2 |> Set.toArray
// compute a padded fvcom bounding box for cropping roms grids
let private mkCropBox (box: BBox) : CropBox =
let pad min max =
let d = 5000.0
float min - d, float max + d
let xlim = pad box.minX box.maxX
let ylim = pad box.minY box.maxY
xlim, ylim
// make an array of indeces of wet roms grid points inside the fvcom domain
let private genCullIdx (box: CropBox) (wet: Mask) (p: BiPos) =
genCropMask box p |> Array.filter (fun (i, j) -> wet[i, j])
// crop roms grid coordinates based in index mask of active, overlapping points
let private cullCoords (p: BiPos) (idx: (int * int)[]) =
let x, y = p
let x' = idx |> Array.map (fun (i, j) -> x[i, j])
let y' = idx |> Array.map (fun (i, j) -> y[i, j])
Array.zip x' y'
let private createTree (points: Leaf<int>[]) =
let tree = KdTree<float, int> (2, KdTree.Math.DoubleMath ())
points
|> Array.iter (fun a -> tree.Add ([| fst a.Pos; snd a.Pos |], a.Data) |> ignore)
if points.Length > 0 then
tree.Balance ()
else
// Log.Warning $"Empty kd-tree"
()
tree
// make a kd-tree for fast nearest neighbour lookup
let private buildTree (points: (float * float) array) =
points
|> Array.mapi (fun n (x, y) -> { Pos = x, y; Data = n })
// |> create2DTree treeLeafSize
|> createTree
// adjoinIdx: index to culled roms grid for each fvcom node
// cullIdx: index to roms points _actually_ in use
// oobIdx: out-of-bounds points, too far from a roms cell
type FvcomAdjoint = { adjoinIdx: int[]; cullIdx: (int * int)[]; oobIdx: int[] }
let private dist (a: float * float) (b: float * float) =
let x0, y0 = a
let x1, y1 = b
let dX = x1 - x0
let dY = y1 - y0
dX ** 2 + dY ** 2 |> sqrt |> (*) 0.5
let private distLngLat (proj: IProj) (a: float * float) (b: float * float) =
let x0, y0 = a |> proj.project
let x1, y1 = b |> proj.project
let dX = x1 - x0
let dY = y1 - y0
dX ** 2 + dY ** 2 |> sqrt |> (*) 0.5
// let private genOobIdx (proj: IProj) (tree: Tree<_, _>) (cullIdx: (int * int) []) (fPos: PosVec) (rPos: BiPos) =
// let rLon, rLat = rPos
// let dMax =
// distLngLat proj (rLon[0, 0], rLat[0, 0]) (rLon[0, 1], rLat[0, 1])
// fPos
// |> Array.fold
// (fun (n, a) (x, y as p0) ->
// nearestNeighbor tree { X = x; Y = y }
// |> fun p ->
// let i0, i1 = cullIdx[p.Value.Data]
// let p1 = proj.project ((rLon[i0, i1], rLat[i0, i1]))
// let d = dist p0 p1
// if d > dMax then n + 1, n :: a else n + 1, a)
// (0, [])
// |> snd
// |> Array.ofList
let private genOobIdx (proj: IProj) (tree: KdTree<_, _>) (cullIdx: (int * int)[]) (fPos: PosVec) (rPos: BiPos) =
let rLon, rLat = rPos
let dMax = distLngLat proj (rLon[0, 0], rLat[0, 0]) (rLon[0, 1], rLat[0, 1])
fPos
|> Array.fold
(fun (n, a) (x, y as p0) ->
tree.GetNearestNeighbours ([| x; y |], 1)
|> Array.head
|> fun p ->
let i0, i1 = cullIdx[p.Value]
let p1 = proj.project ((rLon[i0, i1], rLat[i0, i1]))
let d = dist p0 p1
if d > dMax then n + 1, n :: a else n + 1, a)
(0, [])
|> snd
|> Array.ofList
// compute the nearest neighbour index into the cropped roms grid
// let private mkFvcomAdjoint (proj: IProj) (fPos: PosVec) (bbox: BBox) ((rPos, wet): BiPos * Mask) =
// let box = mkCropBox bbox
// let pos' = reProject proj rPos
// let cullIdx = genCullIdx box wet pos'
// let tree = cullCoords pos' cullIdx |> buildTree
// let nearest =
// fPos
// |> Array.map (fun (x, y) ->
// nearestNeighbor tree { X = x; Y = y }
// |> fun x -> x.Value.Data)
// {
// adjoinIdx = nearest
// cullIdx = cullIdx
// oobIdx = genOobIdx proj tree cullIdx fPos rPos
// }
let private mkFvcomAdjoint (proj: IProj) (fPos: PosVec) (bbox: BBox) ((rPos, wet): BiPos * Mask) =
let box = mkCropBox bbox
let pos' = reProject proj rPos
let cullIdx = genCullIdx box wet pos'
let tree = cullCoords pos' cullIdx |> buildTree
let nearest =
fPos
|> Array.map (fun (x, y) -> tree.GetNearestNeighbours ([| x; y |], 1) |> Array.head |> (fun x -> x.Value))
let oobIdx = genOobIdx proj tree cullIdx fPos rPos
{ adjoinIdx = nearest; cullIdx = cullIdx; oobIdx = oobIdx }
let inline float2 x = bimap float float x
let getNearestNode (proj: IProj) (fvcom: Grid) (roms: BiPos * Mask) =
let fvNodes = Array.map float2 fvcom.Nodes
mkFvcomAdjoint proj fvNodes fvcom.BBox roms
// same as getNearestNode, but for uv in the center of a cell/element
let getNearestCell (proj: IProj) (fvcom: Grid) (roms: BiPos * Mask) =
let cells = Util.calcCentroids fvcom |> Array.map float2
mkFvcomAdjoint proj cells fvcom.BBox roms
let private createIdxTree (points: Leaf<int * int>[]) =
let tree = KdTree<float, int * int> (2, KdTree.Math.DoubleMath ())
points
|> Array.iter (fun a -> tree.Add ([| fst a.Pos; snd a.Pos |], a.Data) |> ignore)
if points.Length > 0 then
tree.Balance ()
else
//Log.Warning $"Empty kd-tree"
()
tree
// rPos must be in the right projection!
let makeNestTree ((lng, lat): float[,] * float[,]) =
[| 0 .. (Array2D.length1 lng) - 1 |]
|> Array.map (fun i ->
[| 0 .. (Array2D.length2 lng) - 1 |]
|> Array.map (fun j ->
let x = lng[i, j]
let y = lat[i, j]
{ Pos = x, y; Data = (i, j) }))
|> Array.concat
// |> create2DTree treeLeafSize
|> createIdxTree
let getNearestUpperLeft' (tree: KdTree<float, int * int>) (((ew, ns), mask): BiPos * Mask) ((x, y): float * float) =
let i, j =
// nearestNeighbor tree { X = x; Y = y }
tree.GetNearestNeighbours ([| x; y |], 1) |> fun x -> x[0].Value
let p1 = [|
ew[i, j], ns[i, j]
ew[i + 1, j], ns[i + 1, j]
ew[i + 1, j - 1], ns[i + 1, j - 1]
ew[i, j - 1], ns[i, j - 1]
|] // lower right grid cell
let p2 = [|
ew[i, j], ns[i, j]
ew[i + 1, j], ns[i + 1, j]
ew[i + 1, j + 1], ns[i + 1, j + 1]
ew[i, j + 1], ns[i, j + 1]
|] // upper right grid cell
let p3 = [|
ew[i, j], ns[i, j]
ew[i - 1, j], ns[i - 1, j]
ew[i - 1, j - 1], ns[i - 1, j - 1]
ew[i, j - 1], ns[i, j - 1]
|] // lower left grid cell
let p4 = [|
ew[i, j], ns[i, j]
ew[i - 1, j], ns[i - 1, j]
ew[i - 1, j + 1], ns[i - 1, j + 1]
ew[i, j + 1], ns[i, j + 1]
|] // upper left grid cell
let q =
if inpolygon p1 (x, y) then
[| i + 1, j - 1; i + 1, j; i, j; i, j - 1 |]
elif inpolygon p2 (x, y) then
[| i + 1, j; i + 1, j + 1; i, j + 1; i, j |]
elif inpolygon p3 (x, y) then
[| i, j - 1; i, j; i - 1, j; i - 1, j - 1 |]
elif inpolygon p4 (x, y) then
[| i, j; i, j + 1; i - 1, j + 1; i - 1, j |]
else
[| 0, 0; 0, 0; 0, 0; 0, 0 |]
if (q |> Array.map (fun (q1, q2) -> q1 + q2) |> Array.sum) = 0 then
failwith "Surrounding grid cell not found"
let m = q |> Array.map (fun (n, m) -> mask[n, m])
Array.zip q m
// TODO: this is the F# Kd-tree version, which is currently defunct
// let getNearestUpperLeft
// (tree: Tree<Leaf<float, int * int> [], Node<float>>)
// (((ew, ns), mask): BiPos * Mask)
// ((x, y): float * float)
// =
// let i, j =
// nearestNeighbor tree { X = x; Y = y }
// |> fun x -> x.Value.Data
// let p1 =
// [|
// ew[i, j], ns[i, j]
// ew[i + 1, j], ns[i + 1, j]
// ew[i + 1, j - 1], ns[i + 1, j - 1]
// ew[i, j - 1], ns[i, j - 1]
// |] // lower right grid cell
// let p2 =
// [|
// ew[i, j], ns[i, j]
// ew[i + 1, j], ns[i + 1, j]
// ew[i + 1, j + 1], ns[i + 1, j + 1]
// ew[i, j + 1], ns[i, j + 1]
// |] // upper right grid cell
// let p3 =
// [|
// ew[i, j], ns[i, j]
// ew[i - 1, j], ns[i - 1, j]
// ew[i - 1, j - 1], ns[i - 1, j - 1]
// ew[i, j - 1], ns[i, j - 1]
// |] // lower left grid cell
// let p4 =
// [|
// ew[i, j], ns[i, j]
// ew[i - 1, j], ns[i - 1, j]
// ew[i - 1, j + 1], ns[i - 1, j + 1]
// ew[i, j + 1], ns[i, j + 1]
// |] // upper left grid cell
// let q =
// if inpolygon p1 (x, y) then
// [| i + 1, j - 1; i + 1, j; i, j; i, j - 1 |]
// elif inpolygon p2 (x, y) then
// [| i + 1, j; i + 1, j + 1; i, j + 1; i, j |]
// elif inpolygon p3 (x, y) then
// [| i, j - 1; i, j; i - 1, j; i - 1, j - 1 |]
// elif inpolygon p4 (x, y) then
// [| i, j; i, j + 1; i - 1, j + 1; i - 1, j |]
// else
// [| 0, 0; 0, 0; 0, 0; 0, 0 |]
// if (q
// |> Array.map (fun (q1, q2) -> q1 + q2)
// |> Array.sum) = 0 then
// failwith "Surrounding grid cell not found"
// let m = q |> Array.map (fun (n, m) -> mask[n, m])
// Array.zip q m
let getNearestCellCorner (coords, _ as grid: BiPos * Mask) (pos: (float * float)[]) =
let tree = makeNestTree coords
// pos |> Array.map (getNearestUpperLeft tree grid)
pos |> Array.map (getNearestUpperLeft' tree grid)
let getCellBox ((ew, ns): BiPos) (boxid: ((int * int) * bool)[]) =
boxid
|> Array.map (fun ((n, m), mask) -> (ew[n, m], ns[n, m]), mask)
|> Array.unzip
let getCellProps (prop: float[,]) ((n, m): int * int) =
prop[n, m], prop[n + 1, m], prop[n + 1, m + 1], prop[n + 1, m]
// pick out elements actually in use
let inline private cullBiMatrix (culler: (int * int)[]) (m: 'a[,]) =
culler |> Array.map (fun (i, j) -> m[i, j])
// adjoin fvcom and roms data based on nearest neighbours
let adjoinMatrix (adj: FvcomAdjoint) (m: 'a[,]) =
let x = cullBiMatrix adj.cullIdx m
adj.adjoinIdx |> Array.map (fun n -> x[n])

260
src/Arome.fs Normal file
View File

@@ -0,0 +1,260 @@
module Oceanbox.FvcomKit.Arome
open System
open Microsoft.Research.Science.Data
open ProjNet.FSharp
open Serilog
open Types
let trans = makeTransform CoordSys.WGS84 (CoordSys.LCCMet ())
[<Struct>]
type Pointf = {
x: float
y: float
} with
static member Zero = { x = 0.0; y = 0.0 }
static member OfTuple (x, y) = { x = x; y = y }
static member OfStructTuple struct (x, y) = { x = x; y = y }
/// Single precision
[<Struct>]
type Points = {
x: single
y: single
} with
static member Zero = { x = 0.0f; y = 0.0f }
static member OfTuple (x, y) = { x = x; y = y }
static member OfStructTuple struct (x, y) = { x = x; y = y }
module Pointf =
let ofPoints (p: Points) : Pointf = { x = float p.x; y = float p.y }
let getBBox (points: Pointf array) : BBox =
let minX = points |> Array.minBy _.x |> _.x
let maxX = points |> Array.maxBy _.x |> _.x
let minY = points |> Array.minBy _.y |> _.y
let maxY = points |> Array.maxBy _.y |> _.y
let center = (minX + (maxX - minX)) / 2., (minY + (maxY - minY)) / 2.
{
minX = minX
maxX = maxX
minY = minY
maxY = maxY
center = center
}
module Points =
let getBBox (points: Points array) : BBox =
let minX = points |> Array.minBy _.x |> _.x
let maxX = points |> Array.maxBy _.x |> _.x
let minY = points |> Array.minBy _.y |> _.y
let maxY = points |> Array.maxBy _.y |> _.y
let center = float (minX + (maxX - minX)) / 2., float (minY + (maxY - minY)) / 2.
{
minX = float minX
maxX = float maxX
minY = float minY
maxY = float maxY
center = center
}
[<Struct>]
type SquareGrid = {
dimensions: int * int
BBox: BBox
squareSize: float
projection: ProjNet.CoordinateSystems.CoordinateSystem
points: Points array
} with
member this.getBoundingBox() = this.BBox
static member empty = {
dimensions = 0, 0
BBox = BBox.empty
squareSize = 0.0
projection = CoordSys.LCCMet ()
points = Array.empty
}
let private square x = x * x
let haversineDistance (earthRadius: float) (x0: float) (y0: float) (x1: float) (y1: float) : float =
let mutable lat1 = y0
let mutable lat2 = y1
let lon1 = x0
let lon2 = x1
let dLat = Double.DegreesToRadians (lat2 - lat1)
let dLon = Double.DegreesToRadians (lon2 - lon1)
lat1 <- Double.DegreesToRadians lat1
lat2 <- Double.DegreesToRadians lat2
let a = square (Math.Sin (dLat / 2.0)) + Math.Cos lat1 * Math.Cos lat2 * square (Math.Sin (dLon / 2.0))
let c = 2.0 * Math.Asin (Math.Sqrt a)
let result = earthRadius * c
result
let getGrid (ds: DataSet) : Result<SquareGrid, string> =
try
let dimensions = ds.Dimensions["x"].Length, ds.Dimensions["y"].Length
let longs : float array2d = ds["longitude"].GetData () :?> float[,]
let lats : float array2d = ds["latitude"].GetData () :?> float[,]
// NOTE: The netcdf file dimensions are defined as (y, x)
let width = Array2D.length2 longs
let height = Array2D.length1 lats
let points : Points array =
let result = Array.create (width * height) Points.Zero
for i in 0 .. height - 1 do
for j in 0 .. width - 1 do
let lat = lats[i, j]
let lon = longs[i, j]
let p = lon, lat
// NOTE(simkir): Convert to lambert projection
let x, y = trans.project p
result[i * width + j] <- Points.OfTuple (single x, single y)
result
let bbox = Points.getBBox points
if points.Length < 2 then
Error "The dataset must contain at least 1 square"
else
let p0 = points[0 * width + 0]
let p1 = points[0 * width + 1]
let p2 = points[1 * width + 0]
// let p3 = points[1 * width + 1]
let x1, x0 = if p1.x > p0.x then p1.x, p0.x else p0.x, p1.x
let y1, y0 = if p2.y > p1.y then p2.y, p1.y else p1.y, p2.y
let lengthX = x1 - x0
let lengthY = y1 - y0
let isSquare = lengthX = lengthY
if not isSquare then
Log.Warning (
"FvcomKit.Arome.getGrid grid is not square: {X1} - {X0} = {LengthX} = {LengthY} = {Y1} - {Y0}",
x1,
x0,
lengthX,
lengthY,
y1,
y0
)
Ok {
SquareGrid.empty with
dimensions = dimensions
BBox = bbox
squareSize = float lengthX
points = points
}
with exn ->
Log.Error (exn, "Sorcerer.Arome.getAromeSquareGrid exception")
Error $"Error reading arome grid: {exn.Message}"
let private getBBox (xs: float array) (ys: float array) : BBox =
try
let minX = xs |> Array.min
let maxX = xs |> Array.max
let minY = ys |> Array.min
let maxY = ys |> Array.max
let center = (minX + (maxX - minX)) / 2., (minY + (maxY - minY)) / 2.
{
minX = minX
maxX = maxX
minY = minY
maxY = maxY
center = center
}
with e ->
Log.Error $"{e}"
BBox.empty
/// Depends on the netcdf having the 'x' and 'y' variables
let getSquareGrid (ds: DataSet) : Result<SquareGrid, string> =
try
let dimensions = ds.Dimensions["x"].Length, ds.Dimensions["y"].Length
let xs = (ds["x"].GetData () :?> single[]) |> Array.map float
let ys = (ds["y"].GetData () :?> single[]) |> Array.map float
let bbox = getBBox xs ys
if xs.Length < 2 && ys.Length < 2 then
Error "The dataset must contain at least 1 square"
else
let x0, y0 = xs[0], ys[0]
let x1, y1 = xs[1], ys[1]
let lengthX = if x1 > x0 then x1 - x0 else x0 - x1
let lengthY = if y1 > y0 then y1 - y0 else y0 - y1
let isSquare = lengthX = lengthY
if isSquare then
{ SquareGrid.empty with dimensions = dimensions; BBox = bbox; squareSize = lengthX }
|> Ok
else
Log.Error (
"FvcomKit.Arome.getGrid grid is not square: {X1} - {X0} = {LengthX} = {LengthY} = {Y1} - {Y0}",
x1,
x0,
lengthX,
lengthY,
y1,
y0
)
Error "The given data set does not contain a grid made up of squares"
with exn ->
Log.Error (exn, "FvcomKit.Arome.getGrid exception")
Error $"Error reading arome grid: {exn.Message}"
let readUV (ds: DataSet) (t: int) x y : single * single =
let xWind = ds["x_wind_10m"]
let yWind = ds["y_wind_10m"]
let origin = [| t; 0; y; x |]
let shape = [| 1; 1; 1; 1; |]
Log.Verbose("""Fetching NetCDF["x_wind_10m"]({Origin}, {Shape})""", origin, shape)
let us = xWind.GetData (origin, shape) :?> single[,,,]
let vs = yWind.GetData (origin, shape) :?> single[,,,]
us[0, 0, 0, 0], vs[0, 0, 0, 0]
/// Finds the index of a tile within a square grid, given its bounding box and square length
let tryFindIndex (grid: SquareGrid) (x0, y0) =
let wide, tall = grid.dimensions
let bbox = grid.BBox
if bbox.minX <= x0 && x0 < bbox.maxX && bbox.minY <= y0 && y0 < bbox.maxY then
let dx = x0 - bbox.minX
let dy = y0 - bbox.minY
let xIdx = int (dx / grid.squareSize)
let yIdx = int (dy / grid.squareSize)
if xIdx < wide && yIdx < tall then
Some (xIdx, yIdx)
else
Log.Warning (
"Got wrong indices within the bounding box of the archive: min {@Min}, max {@Max}, point {@Point}, delta {@Delta}m, indices {@Indices}",
(bbox.minX, bbox.minY),
(bbox.maxX, bbox.maxY),
(x0, y0),
(dx, dy),
(xIdx, yIdx)
)
None
else
None
/// Tries to get the closest x and y in the arome dataset based on position p
let tryFind (grid: SquareGrid) (p: float * float) : (int * int) option =
tryFindIndex grid p
let tryFindWithProj (proj: Projection) (grid: SquareGrid) (p0: float, p1: float) : (int * int) option =
let coordSys : ProjNet.CoordinateSystems.CoordinateSystem = Projection.ToCoordinateSystem proj
let trans = makeTransform coordSys grid.projection
let p = trans.project ((p0, p1))
tryFindIndex grid p

View File

@@ -2,185 +2,486 @@ module Oceanbox.FvcomKit.Fvcom
#nowarn "57"
open System
open Microsoft.Research.Science.Data
open FSharpPlus
open Serilog
open Grid
type FvcomRestart =
{
salinity: single [,,]
temp: single [,,]
zeta: single [,]
u: single [,,]
v: single [,,]
ua: single [,]
va: single [,]
open Types
type FvcomGrid = {
Elem: Elem array
Nodes: Node array
BBox: BBox
Cells: Node array
Bathymetry: single[]
Siglay: single[,]
SiglayCenter: single[,]
Siglev: single[,]
} with
interface IGrid with
member this.getVertex n = this.Nodes[n]
member this.getCell n = this.Elem[n]
member this.getCellVertices n =
let a, b, c = this.Elem[n]
this.Nodes[a], this.Nodes[b], this.Nodes[c]
member this.getVertices() = this.Nodes
member this.getCells() = this.Elem
member this.getBoundingBox() = this.BBox
static member empty = {
Elem = Array.empty
Nodes = Array.empty
BBox = BBox.empty
Cells = Array.empty
Bathymetry = Array.empty
Siglay = Array2D.zeroCreate 0 0
SiglayCenter = Array2D.zeroCreate 0 0
Siglev = Array2D.zeroCreate 0 0
}
type FvcomGrid =
{
Elem: Elem array
Nodes: Node array
BBox: BBox
Cells: Node array
Bathymetry: single []
Siglay: single [,]
SiglayCenter: single [,]
Siglev: single [,]
}
static member empty =
{
Elem = Array.empty
Nodes = Array.empty
BBox = BBox.empty
Cells = Array.empty
Bathymetry = Array.empty
Siglay = Array2D.zeroCreate 0 0
SiglayCenter = Array2D.zeroCreate 0 0
Siglev = Array2D.zeroCreate 0 0
}
member this.ToGrid() =
{
Elem = this.Elem
Nodes = this.Nodes
BBox = this.BBox
}
member this.ToGrid() = { Elem = this.Elem; Nodes = this.Nodes; BBox = this.BBox }
let getNumFrames (ds: DataSet) =
try
ds.Dimensions["time"].Length
with e ->
Log.Error $"{e}"
0
let getNumSiglay (ds: DataSet) =
try
ds.Dimensions["siglay"].Length
with e ->
Log.Error $"{e}"
0
let getNumSiglev (ds: DataSet) =
try
ds.Dimensions["siglev"].Length
with e ->
Log.Error $"{e}"
0
let getTime (ds: DataSet) n =
try
let ts = ds["Times"].GetData () :?> byte[,]
ts[n, *] |> Array.map char |> System.String |> System.DateTime.Parse |> Some
with e ->
Log.Error $"getTime exception: {e.Message}"
None
let getTimeSpanSinceStart (ds: DataSet) n =
try
let days = ds["Itime"].GetData () :?> int[]
let msec = ds["Itime2"].GetData () :?> int[]
let t0 = TimeSpan.FromDays days[n]
let t1 = TimeSpan.FromMilliseconds (float msec[n])
t0 + t1 |> Some
with e ->
Log.Error $"getTimeInDays exception: {e.Message}"
None
let readTauc (ds: DataSet) t =
try
let n = ds.Dimensions["nele"].Length
let tauc = ds["tauc"].GetData ([| t; 0 |], [| 1; n |]) :?> single[,]
tauc[0, *]
with e ->
Log.Error $"{e}"
Array.empty
let readUV (ds: DataSet) t l =
try
let n = ds.Dimensions["nele"].Length
let u = ds[ "u" ].GetData([| t; l; 0 |], [| 1; 1; n |]) :?> single [,,]
let v = ds[ "v" ].GetData([| t; l; 0 |], [| 1; 1; n |]) :?> single [,,]
let u = ds["u"].GetData ([| t; l; 0 |], [| 1; 1; n |]) :?> single[,,]
let v = ds["v"].GetData ([| t; l; 0 |], [| 1; 1; n |]) :?> single[,,]
Array.zip u[0, 0, *] v[0, 0, *]
with
| e ->
with e ->
Log.Error $"{e}"
Array.empty
let readUV' (ds: DataSet) t l =
readUV ds t l
|> Array.collect (fun (x, y) -> [| x; y |])
readUV ds t l |> Array.collect (fun (x, y) -> [| x; y |])
let readUVs (ds: DataSet) t l es =
try
let n = ds.Dimensions["nele"].Length
let u = ds["u"].GetData ([| t; l; 0 |], [| 1; 1; n |]) :?> single[,,]
let v = ds["v"].GetData ([| t; l; 0 |], [| 1; 1; n |]) :?> single[,,]
let u' = es |> Array.map (fun i -> u[0, 0, i])
let v' = es |> Array.map (fun i -> v[0, 0, i])
Array.zip u' v'
with e ->
Log.Error $"{e}"
Array.empty
/// <summary>
/// Reads properties 'u' and 'v' from FVCOM dataset at the given time and depth.
/// </summary>
/// <param name="ds">The NetCDF dataset to open</param>
/// <param name="t">The given time frame</param>
/// <param name="l">The layer of depth from where to read the values</param>
/// <param name="e0">The starting index of the uv range</param>
/// <param name="en">The ending index</param>
/// <returns>Array of tuples, and empty on any errors.</returns>
let readUVRange (ds: DataSet) t l e0 en =
try
let u = ds["u"].GetData ([| t; l; e0 |], [| 1; 1; en |]) :?> single[,,]
let v = ds["v"].GetData ([| t; l; e0 |], [| 1; 1; en |]) :?> single[,,]
Array.zip u[0, 0, *] v[0, 0, *]
with e ->
Log.Error (
e,
"FvcomKit.Fvcom.readUVRange exception with input: time {Time}, layer {Layer}, starting elem {Elem0} and ending elem {ElemN}",
t,
l,
e0,
en
)
Array.empty
let readOmega (ds: DataSet) t l =
try
let n = ds.Dimensions["node"].Length
let omega = ds["omega"].GetData ([| t; l; 0 |], [| 1; 1; n |]) :?> single[,,]
omega[0, 0, *]
with e ->
Log.Error $"{e}"
Array.empty
let readOmegaBlock (ds: DataSet) t =
try
let n = ds.Dimensions["node"].Length
let l = ds.Dimensions["siglev"].Length
let u = ds["omega"].GetData ([| t; 0; 0 |], [| 1; l; n |]) :?> single[,,]
u[0, *, *]
with e ->
Log.Error $"{e}"
Array2D.zeroCreate 0 0
let readOmegas (ds: DataSet) t l es =
try
let n = ds.Dimensions["node"].Length
let omega = ds["omega"].GetData ([| t; l; 0 |], [| 1; 1; n |]) :?> single[,,]
es |> Array.map (fun i -> omega[0, 0, i])
with e ->
Log.Error $"{e}"
Array.empty
let readU (ds: DataSet) t l =
try
let n = ds.Dimensions["nele"].Length
let u = ds["u"].GetData ([| t; l; 0 |], [| 1; 1; n |]) :?> single[,,]
u[0, 0, *]
with e ->
Log.Error $"{e}"
Array.empty
let readUBlock (ds: DataSet) t =
try
let n = ds.Dimensions["nele"].Length
let l = ds.Dimensions["siglay"].Length
let u = ds["u"].GetData ([| t; 0; 0 |], [| 1; l; n |]) :?> single[,,]
u[0, *, *]
with e ->
Log.Error $"{e}"
Array2D.zeroCreate 0 0
let readV (ds: DataSet) t l =
try
let n = ds.Dimensions["nele"].Length
let v = ds["v"].GetData ([| t; l; 0 |], [| 1; 1; n |]) :?> single[,,]
v[0, 0, *]
with e ->
Log.Error $"{e}"
Array.empty
let readVBlock (ds: DataSet) t =
try
let n = ds.Dimensions["nele"].Length
let l = ds.Dimensions["siglay"].Length
let v = ds["v"].GetData ([| t; 0; 0 |], [| 1; l; n |]) :?> single[,,]
v[0, *, *]
with e ->
Log.Error $"{e}"
Array2D.zeroCreate 0 0
let readWw (ds: DataSet) t l =
try
let n = ds.Dimensions["nele"].Length
let ww = ds["ww"].GetData ([| t; l; 0 |], [| 1; 1; n |]) :?> single[,,]
ww[0, 0, *]
with e ->
Log.Error $"{e}"
Array.empty
let readWwBlock (ds: DataSet) t =
try
let n = ds.Dimensions["nele"].Length
let l = ds.Dimensions["siglay"].Length
let w = ds["ww"].GetData ([| t; 0; 0 |], [| 1; l; n |]) :?> single[,,]
w[0, *, *]
with e ->
Log.Error $"{e}"
Array2D.zeroCreate 0 0
let readWws (ds: DataSet) t l es =
try
let n = ds.Dimensions["nele"].Length
let ww = ds["ww"].GetData ([| t; l; 0 |], [| 1; 1; n |]) :?> single[,,]
es |> Array.map (fun i -> ww[0, 0, i])
with e ->
Log.Error $"{e}"
Array.empty
let readUVW (ds: DataSet) t l =
try
let n = ds.Dimensions["nele"].Length
let u = ds["u"].GetData ([| t; l; 0 |], [| 1; 1; n |]) :?> single[,,]
let v = ds["v"].GetData ([| t; l; 0 |], [| 1; 1; n |]) :?> single[,,]
let w = ds["ww"].GetData ([| t; l; 0 |], [| 1; 1; n |]) :?> single[,,]
Array.zip3 u[0, 0, *] v[0, 0, *] w[0, 0, *]
with e ->
Log.Error $"{e}"
Array.empty
let readUVWs (ds: DataSet) t l es =
try
let n = ds.Dimensions["nele"].Length
let u = ds["u"].GetData ([| t; l; 0 |], [| 1; 1; n |]) :?> single[,,]
let v = ds["v"].GetData ([| t; l; 0 |], [| 1; 1; n |]) :?> single[,,]
let w = ds["ww"].GetData ([| t; l; 0 |], [| 1; 1; n |]) :?> single[,,]
let u' = es |> Array.map (fun i -> u[0, 0, i])
let v' = es |> Array.map (fun i -> v[0, 0, i])
let w' = es |> Array.map (fun i -> w[0, 0, i])
Array.zip3 u' v' w'
with e ->
Log.Error $"{e}"
Array.empty
let readUVWRange (ds: DataSet) t l e0 en =
try
let u = ds["u"].GetData ([| t; l; e0 |], [| 1; 1; en |]) :?> single[,,]
let v = ds["v"].GetData ([| t; l; e0 |], [| 1; 1; en |]) :?> single[,,]
let w = ds["ww"].GetData ([| t; l; e0 |], [| 1; 1; en |]) :?> single[,,]
Array.zip3 u[0, 0, *] v[0, 0, *] w[0, 0, *]
with e ->
Log.Error $"{e}"
Array.empty
let readTemp (ds: DataSet) t l =
try
let n = ds.Dimensions["node"].Length
let temp =
ds[ "temp" ].GetData([| t; l; 0 |], [| 1; 1; n |]) :?> single [,,]
let temp = ds["temp"].GetData ([| t; l; 0 |], [| 1; 1; n |]) :?> single[,,]
temp[0, 0, *]
with
| e ->
with e ->
Log.Error $"{e}"
Array.empty
let readTempBlock (ds: DataSet) t =
try
let n = ds.Dimensions["node"].Length
let l = ds.Dimensions["siglay"].Length
let sal = ds["temp"].GetData ([| t; 0; 0 |], [| 1; l; n |]) :?> single[,,]
sal[0, *, *]
with e ->
Log.Error $"{e}"
Array2D.zeroCreate 0 0
let readSalinity (ds: DataSet) t l =
try
let n = ds.Dimensions["node"].Length
let sal =
ds["salinity"]
.GetData([| t; l; 0 |], [| 1; 1; n |])
:?> single [,,]
let sal = ds["salinity"].GetData ([| t; l; 0 |], [| 1; 1; n |]) :?> single[,,]
sal[0, 0, *]
with
| e ->
with e ->
Log.Error $"{e}"
Array.empty
let readSalinityBlock (ds: DataSet) t =
try
let n = ds.Dimensions["node"].Length
let l = ds.Dimensions["siglay"].Length
let sal = ds["salinity"].GetData ([| t; 0; 0 |], [| 1; l; n |]) :?> single[,,]
sal[0, *, *]
with e ->
Log.Error $"{e}"
Array2D.zeroCreate 0 0
let readArt1 (ds: DataSet) =
try
ds[ "art1" ].GetData() :?> single []
with
| e ->
ds["art1"].GetData () :?> single[]
with e ->
Log.Error $"{e}"
Array.empty
let readZeta (ds: DataSet) t =
try
let n = ds.Dimensions["node"].Length
let zeta = ds["zeta"].GetData ([| t; 0 |], [| 1; n |]) :?> single[,]
zeta[0, *]
with e ->
Log.Error $"{e}"
Array.empty
let readBathymetry (ds: DataSet) =
try
ds[ "h" ].GetData() :?> single []
with
| e ->
ds["h"].GetData () :?> single[]
with e ->
Log.Error e.Message
Array.empty
let tryReadBathymetry (ds: DataSet) =
if ds.Variables.Contains "h" then
ds["h"].GetData () :?> single[] |> Some
else
None
let readBathymetryAtCenters (ds: DataSet) =
let h = readBathymetry ds
let lc = ds.Dimensions["nele"].Length
try
let nv = ds["nv"].GetData () :?> int[,] |> Array2D.map (fun n -> n - 1)
[| 0 .. lc - 1 |]
|> Array.map (fun i -> (h[nv[0, i]] + h[nv[1, i]] + h[nv[2, i]]) / 3.0f)
with e ->
Log.Error e.Message
Array.empty
let readBathymetryAtCenter (ds: DataSet) e =
try
let h = ds[ "h_center" ].GetData() :?> single []
let h = readBathymetryAtCenters ds
h[e]
with
| e ->
with e ->
Log.Error e.Message
-1f
let readSiglev (ds: DataSet) n =
try
let siglev = ds[ "siglev" ].GetData() :?> single [,]
siglev[*, n]
with
| err ->
let l = ds.Dimensions["siglev"].Length
let siglev = ds["siglev"].GetData ([| 0; n |], [| l; 1 |]) :?> single[,]
siglev[*, 0]
with err ->
Log.Error $"{err}"
Array.empty
let tryReadSiglev (ds: DataSet) n =
if ds.Variables.Contains "siglev" then
let l = ds.Dimensions["siglev"].Length
let siglev = ds["siglev"].GetData ([| 0; n |], [| l; 1 |]) :?> single[,]
Some siglev[*, 0]
else
None
let readSiglay (ds: DataSet) n =
try
let siglay = ds[ "siglay" ].GetData() :?> single [,]
siglay[*, n]
with
| err ->
let l = ds.Dimensions["siglay"].Length
let siglay = ds["siglay"].GetData ([| 0; n |], [| l; 1 |]) :?> single[,]
siglay[*, 0]
with err ->
Log.Error $"{err}"
Array.empty
let readSiglayCenter (ds: DataSet) n =
try
let siglay = ds[ "siglay_center" ].GetData() :?> single [,]
siglay[*, n]
with
| err ->
Log.Error $"{err}"
Array.empty
let tryReadSiglay (ds: DataSet) n =
if ds.Variables.Contains "siglay" then
let l = ds.Dimensions["siglay"].Length
let siglay = ds["siglay"].GetData ([| 0; n |], [| l; 1 |]) :?> single[,]
Some siglay[*, 0]
else
None
let readSiglevAtCenter (ds: DataSet) e =
try
let siglev = ds[ "siglev_center" ].GetData() :?> single [,]
siglev[*, e]
with
| e ->
let nv = ds["nv"].GetData () :?> int[,] |> Array2D.map (fun n -> n - 1)
let s1 = readSiglev ds nv[0, e]
let s2 = readSiglev ds nv[1, e]
let s3 = readSiglev ds nv[2, e]
[| 0 .. s1.Length - 1 |] |> Array.map (fun i -> (s1[i] + s2[i] + s3[i]) / 3.0f)
with e ->
Log.Error $"{e}"
Array.empty
let readSiglayAtCenter (ds: DataSet) e =
try
let siglay = ds[ "siglay_center" ].GetData() :?> single [,]
siglay[*, e]
with
| e ->
let nv = ds["nv"].GetData () :?> int[,] |> Array2D.map (fun n -> n - 1)
let s1 = readSiglay ds nv[0, e]
let s2 = readSiglay ds nv[1, e]
let s3 = readSiglay ds nv[2, e]
[| 0 .. s1.Length - 1 |] |> Array.map (fun i -> (s1[i] + s2[i] + s3[i]) / 3.0f)
with e ->
Log.Error $"{e}"
Array.empty
let tryReadSiglayAtCenter (ds: DataSet) e =
if ds.Variables.Contains "siglay" then
let nv = ds["nv"].GetData () :?> int[,] |> Array2D.map (fun n -> n - 1)
let s1 = readSiglay ds nv[0, e]
let s2 = readSiglay ds nv[1, e]
let s3 = readSiglay ds nv[2, e]
[| 0 .. s1.Length - 1 |]
|> Array.map (fun i -> (s1[i] + s2[i] + s3[i]) / 3.0f)
|> Some
else
None
// deprecated
let readSiglayCenter = readSiglayAtCenter
module Siglay =
let readSiglay (ds: DataSet) =
try
ds[ "siglay" ].GetData() :?> single [,]
with
| err ->
ds["siglay"].GetData () :?> single[,]
with err ->
Log.Error $"{err}"
Array2D.zeroCreate 0 0
let tryReadSiglay (ds: DataSet) =
if ds.Variables.Contains "siglay" then
ds["siglay"].GetData () :?> single[,] |> Some
else
None
let readSiglayAtCenter (ds: DataSet) =
let siglay = readSiglay ds
try
ds[ "siglay_center" ].GetData() :?> single [,]
with
| e ->
let nv = ds["nv"].GetData () :?> int[,] |> Array2D.map (fun n -> n - 1)
let l1 = Array2D.length1 siglay
let l2 = Array2D.length2 nv
let sc = Array2D.zeroCreate l1 l2
sc
|> Array2D.mapi (fun i j _ -> (siglay[i, nv[0, j]] + siglay[i, nv[1, j]] + siglay[i, nv[2, j]]) / 3.0f)
with e ->
Log.Error $"{e}"
Array2D.zeroCreate 0 0
let tryReadSiglayAtCenter (ds: DataSet) =
if ds.Variables.Contains "siglay" then
readSiglayAtCenter ds |> Some
else
None
let readUv (ds: DataSet) e t =
try
let l = ds.Dimensions["siglay"].Length
let u = ds[ "u" ].GetData([| t; 0; e |], [| 1; l; 1 |]) :?> single [,,]
let v = ds[ "v" ].GetData([| t; 0; e |], [| 1; l; 1 |]) :?> single [,,]
let u = ds["u"].GetData ([| t; 0; e |], [| 1; l; 1 |]) :?> single[,,]
let v = ds["v"].GetData ([| t; 0; e |], [| 1; l; 1 |]) :?> single[,,]
Array.zip u[0, *, 0] v[0, *, 0]
with
| err ->
with err ->
Log.Warning $"readUv {e} {t}"
Log.Error $"{err}"
Array.empty
let readUvw (ds: DataSet) e t =
try
let l = ds.Dimensions["siglay"].Length
let u = ds["u"].GetData ([| t; 0; e |], [| 1; l; 1 |]) :?> single[,,]
let v = ds["v"].GetData ([| t; 0; e |], [| 1; l; 1 |]) :?> single[,,]
let w = ds["ww"].GetData ([| t; 0; e |], [| 1; l; 1 |]) :?> single[,,]
Array.zip3 u[0, *, 0] v[0, *, 0] w[0, *, 0]
with err ->
Log.Warning $"readUv {e} {t}"
Log.Error $"{err}"
Array.empty
@@ -188,12 +489,10 @@ module Siglay =
let readUv' (ds: DataSet) e t =
try
let l = ds.Dimensions["siglay"].Length
let u = ds[ "u" ].GetData([| t; 0; e |], [| 1; l; 1 |]) :?> single [,,]
let v = ds[ "v" ].GetData([| t; 0; e |], [| 1; l; 1 |]) :?> single [,,]
Array.zip u[0, *, 0] v[0, *, 0]
|> Array.collect (fun (x, y) -> [| x; y |])
with
| err ->
let u = ds["u"].GetData ([| t; 0; e |], [| 1; l; 1 |]) :?> single[,,]
let v = ds["v"].GetData ([| t; 0; e |], [| 1; l; 1 |]) :?> single[,,]
Array.zip u[0, *, 0] v[0, *, 0] |> Array.collect (fun (x, y) -> [| x; y |])
with err ->
Log.Warning $"readUv' {e} {t}"
Log.Error $"{err}"
Array.empty
@@ -201,10 +500,9 @@ module Siglay =
let readTemp (ds: DataSet) n t =
try
let l = ds.Dimensions["siglay"].Length
let t = ds[ "temp" ].GetData([| t; 0; n |], [| 1; l; 1 |]) :?> single [,,]
let t = ds["temp"].GetData ([| t; 0; n |], [| 1; l; 1 |]) :?> single[,,]
t[0, *, 0]
with
| err ->
with err ->
Log.Warning $"readTemp {n} {t}"
Log.Error $"{err}"
Array.empty
@@ -212,50 +510,95 @@ module Siglay =
let readSalinity (ds: DataSet) n t =
try
let l = ds.Dimensions["siglay"].Length
let t =
ds["salinity"]
.GetData([| t; 0; n |], [| 1; l; 1 |])
:?> single [,,]
let t = ds["salinity"].GetData ([| t; 0; n |], [| 1; l; 1 |]) :?> single[,,]
t[0, *, 0]
with
| err ->
with err ->
Log.Warning $"readS {n} {t}"
Log.Error $"{err}"
Array.empty
module Singular =
let readUv (ds: DataSet) e t d =
try
let u = ds["u"].GetData ([| t; d; e |], [| 1; 1; 1 |]) :?> single[,,]
let v = ds["v"].GetData ([| t; d; e |], [| 1; 1; 1 |]) :?> single[,,]
u[0, 0, 0], v[0, 0, 0]
with err ->
Log.Warning $"readUv {e} {t}"
Log.Error $"{err}"
0f, 0f
let readTemp (ds: DataSet) n t d : single =
try
let t = ds["temp"].GetData ([| t; d; n |], [| 1; 1; 1 |]) :?> single[,,]
t[0, 0, 0]
with err ->
Log.Warning $"readTemp {n} {t}"
Log.Error $"{err}"
0f
let readSalinity (ds: DataSet) n t d =
try
let t = ds["salinity"].GetData ([| t; d; n |], [| 1; 1; 1 |]) :?> single[,,]
t[0, 0, 0]
with err ->
Log.Warning $"readS {n} {t}"
Log.Error $"{err}"
0f
let readZeta (ds: DataSet) n t =
try
let zeta = ds["zeta"].GetData ([| t; n |], [| 1; 1 |]) :?> single[,]
zeta[0, 0]
with e ->
Log.Error $"{e}"
0f
let getBBox (ds: DataSet) : BBox =
try
let x = ds[ "x" ].GetData() :?> single []
let y = ds[ "y" ].GetData() :?> single []
let x = ds["x"].GetData () :?> single[]
let y = ds["y"].GetData () :?> single[]
let minX = Array.min x
let maxX = Array.max x
let minY = Array.min y
let maxY = Array.max y
let center = minX + (maxX - minX) / 2f, minY + (maxY - minY) / 2f
let center = float (minX + maxX) / 2., float (minY + maxY) / 2.
{
minX = minX
maxX = maxX
minY = minY
maxY = maxY
minX = float minX
maxX = float maxX
minY = float minY
maxY = float maxY
center = center
}
with
| e ->
with e ->
Log.Error $"{e}"
BBox.empty
let getGrid (ds: DataSet) : FvcomGrid =
try
let x = ds[ "x" ].GetData() :?> single []
let y = ds[ "y" ].GetData() :?> single []
let xc = ds[ "xc" ].GetData() :?> single []
let yc = ds[ "yc" ].GetData() :?> single []
let nv = ds[ "nv" ].GetData() :?> int [,]
let x = ds["x"].GetData () :?> single[] |> Array.map float
let y = ds["y"].GetData () :?> single[] |> Array.map float
let xc = ds["xc"].GetData () :?> single[] |> Array.map float
let yc = ds["yc"].GetData () :?> single[] |> Array.map float
let nv = ds["nv"].GetData () :?> int[,]
let h = readBathymetry ds
let siglay = Siglay.readSiglay ds
let siglay_c = Siglay.readSiglayAtCenter ds
let siglev = ds[ "siglev" ].GetData() :?> single [,]
let h =
match tryReadBathymetry ds with
| Some b -> b
| None -> Array.empty
let siglay =
match Siglay.tryReadSiglay ds with
| Some s -> s
| None -> Array2D.zeroCreate 0 0
let siglay_c =
match Siglay.tryReadSiglayAtCenter ds with
| Some s -> s
| None -> Array2D.zeroCreate 0 0
let siglev =
if ds.Variables.Contains "siglev" then
ds["siglev"].GetData () :?> single[,]
else
Array2D.zeroCreate 0 0
let elem =
Array.zip3 nv[0, *] nv[1, *] nv[2, *]
|> Array.map (fun (a, b, c) -> a - 1, b - 1, c - 1)
@@ -271,39 +614,64 @@ let getGrid (ds: DataSet) : FvcomGrid =
SiglayCenter = siglay_c
Siglev = siglev
}
with
| e ->
with e ->
Log.Error $"{e}"
FvcomGrid.empty
let getGridLonLat (ds: DataSet) : FvcomGrid =
try
let x = ds["lon"].GetData () :?> single[] |> Array.map float
let y = ds["lat"].GetData () :?> single[] |> Array.map float
let xc = ds["lonc"].GetData () :?> single[] |> Array.map float
let yc = ds["latc"].GetData () :?> single[] |> Array.map float
let nv = ds["nv"].GetData () :?> int[,]
let h =
match tryReadBathymetry ds with
| Some b -> b
| None -> Array.empty
let siglay =
match Siglay.tryReadSiglay ds with
| Some s -> s
| None -> Array2D.zeroCreate 0 0
let siglay_c =
match Siglay.tryReadSiglayAtCenter ds with
| Some s -> s
| None -> Array2D.zeroCreate 0 0
let siglev =
if ds.Variables.Contains "siglev" then
ds["siglev"].GetData () :?> single[,]
else
Array2D.zeroCreate 0 0
let elem =
Array.zip3 nv[0, *] nv[1, *] nv[2, *]
|> Array.map (fun (a, b, c) -> a - 1, b - 1, c - 1)
let nds = Array.zip x y
let cells = Array.zip xc yc
{
Elem = elem
Nodes = nds
BBox = getBBox ds
Cells = cells
Bathymetry = h
Siglay = siglay
SiglayCenter = siglay_c
Siglev = siglev
}
with e ->
Log.Error $"{e}"
FvcomGrid.empty
let getNbve (ds: DataSet) =
let nbve = ds["nbve"].GetData() :?> int [,]
let nbve = ds["nbve"].GetData () :?> int[,]
[|
for i = 0 to (Array2D.length2 nbve - 1) do
nbve[*, i]
|> Array.filter ((<>) 0)
|> Array.map (fun x -> x - 1)
|> Array.rev
nbve[*, i] |> Array.filter ((<>) 0) |> Array.map (fun x -> x - 1) |> Array.rev
|]
let writeFvcomRestart (fvcom: DataSet) (data: FvcomRestart) =
try
fvcom
.Variables[ "salinity" ]
.PutData data.salinity
fvcom.Variables[ "temp" ].PutData data.temp
fvcom.Variables[ "zeta" ].PutData data.zeta
fvcom.Variables[ "u" ].PutData(data.u)
fvcom.Variables[ "v" ].PutData(data.v)
fvcom.Variables[ "ua" ].PutData(data.ua)
fvcom.Variables[ "va" ].PutData(data.va)
with
| e -> Log.Fatal e.Message
let projectFvcomGrid proj (grid: FvcomGrid) : FvcomGrid =
{ grid with
let projectFvcomGrid proj (grid: FvcomGrid) : FvcomGrid = {
grid with
Nodes = grid.Nodes |> Array.Parallel.map proj
BBox = projectBBox proj grid.BBox
Cells = grid.Cells |> Array.Parallel.map proj
}
}

View File

@@ -3,77 +3,93 @@ module Oceanbox.FvcomKit.Grid
open System
open FSharpPlus
open FSharpPlus.Control
open ProjNet.CoordinateSystems
open Serilog
open ProjNet.FSharp
open MessagePack
open MBrace.FsPickler
//open FsKDTree
open KdTree // NOTE: C# version
open Types
type NodeIdx = int
type ElemIdx = int
type Edge = int * int
type Elem = NodeIdx * NodeIdx * NodeIdx
type Node = single * single
type Node = float * float
type BBox =
{
minX: single
maxX: single
minY: single
maxY: single
center: single * single
}
static member empty =
{
minX = Single.MaxValue
maxX = Single.MinValue
minY = Single.MaxValue
maxY = Single.MinValue
center = 0f, 0f
}
type Pos = float * float
type Leaf<'a> = { Pos: Pos; Data: 'a }
type Field = (float * float) array
type Grid =
{
Elem: Elem array
Nodes: Node array
BBox: BBox
}
static member empty =
{
Elem = Array.empty
Nodes = Array.empty
BBox = BBox.empty
}
type Cell = NodeIdx * NodeIdx * NodeIdx
type ElemsAroundNode = Map<NodeIdx, ElemIdx []>
type NodesAroundNode = Map<NodeIdx, NodeIdx []>
type IGrid =
abstract getVertex: int -> Vertex
abstract getCell: int -> Cell
abstract getCellVertices: int -> Vertex * Vertex * Vertex
abstract getVertices: unit -> Vertex array
abstract getCells: unit -> Cell array
abstract getBoundingBox: unit -> BBox
type NeighborIndex =
{
ElemsAroundNode: ElemsAroundNode
NodesAroundNode: NodesAroundNode
}
static member empty =
{
ElemsAroundNode = Map.empty
NodesAroundNode = Map.empty
}
type Grid = {
Elem: Elem array
Nodes: Node array
BBox: BBox
} with
interface IGrid with
member this.getVertex n = this.Nodes[n]
member this.getCell n = this.Elem[n]
member this.getCellVertices n =
let a, b, c = this.Elem[n]
this.Nodes[a], this.Nodes[b], this.Nodes[c]
member this.getVertices() = this.Nodes
member this.getCells() = this.Elem
member this.getBoundingBox() = this.BBox
static member empty = { Elem = Array.empty; Nodes = Array.empty; BBox = BBox.empty }
type private Ean = Map<NodeIdx, ElemIdx list>
type ElemsAroundNode = Map<NodeIdx, ElemIdx array>
type NodesAroundNode = Map<NodeIdx, NodeIdx array>
type ElemsAroundElem = Map<ElemIdx, ElemIdx array>
type NeighborIndex = {
ElemsAroundNode: ElemsAroundNode
NodesAroundNode: NodesAroundNode
ElemsAroundElem: ElemsAroundElem
} with
static member empty = { ElemsAroundNode = Map.empty; NodesAroundNode = Map.empty; ElemsAroundElem = Map.empty }
// NOTE(SimenLK): The amount of items to be stored in the trees leafs
// let treeLeafSize = LeafNodeSize 64
let private createTree (points: Leaf<int> array) =
let tree = KdTree<float, int> (2, KdTree.Math.DoubleMath ())
do points |> Array.iter (fun a -> tree.Add ([| fst a.Pos; snd a.Pos |], a.Data) |> ignore)
if points.Length > 0 then
do tree.Balance ()
else
do Log.Warning $"Empty kd-tree"
tree
type private Ean = Map<NodeIdx, ElemIdx array>
let private makeElemsSurroundingNodeMap (elem: Elem array) : ElemsAroundNode =
let addElIdx k v (nodes: Ean) =
Map.tryFind k nodes
|> Option.defaultWith (fun () -> [])
|> fun nds -> Map.add k (v :: nds) nodes
elem
|> Array.fold
(fun (n, acc) (a, b, c) ->
let acc' =
acc
|> addElIdx a n
|> addElIdx b n
|> addElIdx c n
n + 1, acc')
(0, Map.empty)
let addElIdx k v (nodes: Ean) : Ean =
let nds =
Map.tryFind k nodes
|> Option.defaultValue [||]
nodes |> Map.add k (Array.append [|v|] nds)
let folder (n, acc) (a, b, c) =
let acc' = acc |> addElIdx a n |> addElIdx b n |> addElIdx c n
n + 1, acc'
((0, Map.empty), elem)
||> Array.fold folder
|> snd
|> Map.mapValues toArray
let private makeElemsSurroundingNodeMap' (elem: Elem array) : ElemsAroundNode =
let addElemIdx k v nodes =
@@ -84,12 +100,9 @@ let private makeElemsSurroundingNodeMap' (elem: Elem array) : ElemsAroundNode =
elem
|> Array.fold
(fun (n, acc) (a, b, c) ->
let acc' =
acc
|> addElemIdx a n
|> addElemIdx b n
|> addElemIdx c n
n + 1, acc')
let acc' = acc |> addElemIdx a n |> addElemIdx b n |> addElemIdx c n
n + 1, acc'
)
(0, Map.empty)
|> snd
|> Map.mapValues toArray
@@ -100,37 +113,51 @@ let private makeNodesSurroudingNodeMap (n2e: ElemsAroundNode) (elem: Elem array)
n
|> Array.collect (fun x ->
let n1, n2, n3 = elem[x]
[| n1; n2; n3 |])
|> Array.distinct)
[| n1; n2; n3 |]
)
|> Array.distinct
)
let private getSurrounding (idx: Map<int, int []>) (a, b, c) =
[| idx[a]; idx[b]; idx[c] |]
|> Array.concat
|> Array.distinct
let private makeElemsSurroundingElemMap (ean: ElemsAroundNode) (elem: Elem array) : ElemsAroundElem =
elem
|> Array.mapi (fun elemIdx (n1, n2, n3) ->
// For each element, find all elements that share any of its nodes
let surroundingElems =
[| ean[n1]; ean[n2]; ean[n3] |]
|> Array.concat
|> Array.distinct
|> Array.filter (fun x -> x <> elemIdx) // Remove self
elemIdx, surroundingElems
)
|> Map.ofArray
let getSurrounding (idx: Map<int, int[]>) (a, b, c) =
[| idx[a]; idx[b]; idx[c] |] |> Array.concat |> Array.distinct
let makeNeighborIndex (grid: IGrid) =
let elem = grid.getCells ()
let ean = makeElemsSurroundingNodeMap elem
let makeNeighborIndex (grid: Grid) =
Log.Information "Indexing grids."
let ean = makeElemsSurroundingNodeMap grid.Elem
{
ElemsAroundNode = ean
NodesAroundNode = makeNodesSurroudingNodeMap ean grid.Elem
NodesAroundNode = makeNodesSurroudingNodeMap ean elem
ElemsAroundElem = makeElemsSurroundingElemMap ean elem
}
|> fun x ->
Log.Debug "...done indexing grids."
x
let getElemsSurroundingNode (idx: NeighborIndex) n = idx.ElemsAroundNode[n]
let getNodesSurroundingNode (idx: NeighborIndex) n = idx.NodesAroundNode[n]
let getNodesSurroundingElem (idx: NeighborIndex) (grid: Grid) e =
getSurrounding idx.NodesAroundNode grid.Elem[e]
let getNodesSurroundingElem (idx: NeighborIndex) e = idx.ElemsAroundElem[e]
// let getNodesSurroundingElem (idx: NeighborIndex) (grid: Grid) e =
// getSurrounding idx.NodesAroundNode grid.Elem[e]
let getElemsSurroundingElem (idx: NeighborIndex) (grid: Grid) e =
getSurrounding idx.ElemsAroundNode grid.Elem[e]
let calcBBoxCenter (x: BBox) =
x.minX + (x.maxX - x.minX) / 2.0f, x.minY + (x.maxY - x.minY) / 2.0f
x.minX + (x.maxX - x.minX) / 2.0, x.minY + (x.maxY - x.minY) / 2.0
let calcBBox nodes =
let x, y = Array.unzip nodes
@@ -139,14 +166,14 @@ let calcBBox nodes =
maxX = Array.max x
minY = Array.min y
maxY = Array.max y
center = 0f, 0f
center = 0, 0
}
|> fun x -> { x with center = calcBBoxCenter x }
let printBBox grid = calcBBox grid.Nodes |> printfn "%A"
let bboxToLngLat b =
let toLatLon = ProjNet.FSharp.UTM_TO_WGS84 33
let bboxToLngLat (coordsys: CoordinateSystem) b =
let toLatLon = makeTransform coordsys CoordSys.WGS84
let x0, y0 = toLatLon.project ((b.minX, b.minY))
let x1, y1 = toLatLon.project ((b.maxX, b.maxY))
// Log.Error (sprintf "(%f, %f) (%f, %f)" x0 y0 x1 y1)
@@ -161,69 +188,94 @@ let bboxToLngLat b =
let projectBBox proj b =
let x0, y0 = proj (b.minX, b.minY)
let x1, y1 = proj (b.maxX, b.maxY)
{ minX = x0; maxX = x1; minY = y0; maxY = y1; center = proj b.center }
let projectGrid proj (grid: Grid) : Grid =
{ grid with
Nodes = grid.Nodes |> Array.Parallel.map proj
BBox = projectBBox proj grid.BBox
{
minX = x0
maxX = x1
minY = y0
maxY = y1
center = proj b.center
}
let toWebMercator (grid: Grid) =
let toWebMercator = ProjNet.FSharp.UTM_TO_EPSG3857 33
let s = System.Diagnostics.Stopwatch.StartNew()
let projectGrid proj (grid: Grid) : Grid = {
grid with
Nodes = grid.Nodes |> Array.Parallel.map proj
BBox = projectBBox proj grid.BBox
}
let rescaleGrid (factor: float) (grid: Grid) : Grid =
let nodes' = grid.Nodes |> Array.Parallel.map (fun (x, y) -> factor * x, factor * y)
let bbox' = calcBBox nodes'
{ grid with Nodes = nodes'; BBox = bbox' }
let translateGrid (x0, y0) grid =
let nodes' = grid.Nodes |> Array.Parallel.map (fun (x, y) -> x + x0, y + y0)
let bbox' = calcBBox nodes'
{ grid with Nodes = nodes'; BBox = bbox' }
let toWebMercator (coordsys: CoordinateSystem) (grid: Grid) =
let toWebMercator = makeTransform coordsys CoordSys.EPSG3857
let s = System.Diagnostics.Stopwatch.StartNew ()
let g = grid |> projectGrid toWebMercator.project
s.Stop()
s.Stop ()
Log.Debug $"Reprojected grid: {s.ElapsedMilliseconds} ms"
g
let toLonLat (coordsys: CoordinateSystem) (grid: Grid) =
let toLonLat = makeTransform coordsys CoordSys.WGS84
let s = System.Diagnostics.Stopwatch.StartNew ()
let g = grid |> projectGrid toLonLat.project
s.Stop ()
Log.Debug $"Reprojected grid: {s.ElapsedMilliseconds} ms"
g
let private chomp (l: string) = l.Split ' ' |> Array.filter ((<>) "")
// TODO: pattern match and compare sizes for more flex
let private readGrdHeader (h: string []) =
let private readGrdHeader (h: string[]) =
let parse x = (chomp x)[3] |> int
try
let nodes = parse h[0]
let nele = parse h[1]
Some (nodes, nele)
with _ -> None
with _ ->
None
let private readObcHeader (h: string) =
try
(chomp h)[4] |> int |> Some
with _ -> None
with _ ->
None
let private reader (parser: string [] -> 'a) (f: string array) =
let private reader (parser: string[] -> 'a) (f: string array) =
try
f |> Array.map (chomp >> parser) |> Some
with
| e ->
with e ->
Log.Error e.Message
None
let private readElem (f: string array) =
let p =
function
| [| _; a; b; c; _ |] -> int a - 1, int b - 1, int c - 1
| [| a; b; c |] -> int a - 1, int b - 1, int c - 1
| _ -> failwith "readElem failed"
| [| _; a; b; c; _ |] -> int a - 1, int b - 1, int c - 1
| [| a; b; c |] -> int a - 1, int b - 1, int c - 1
| _ -> failwith "readElem failed"
reader p f
let private readNodes (f: string array) =
let p =
function
| [| _; x; y; _ |] -> single x, single y
| [| x; y |] -> single x, single y
| x -> failwith $"readNodes failed: %A{x}"
| [| _; x; y; _ |] -> float x, float y
| [| x; y |] -> float x, float y
| x -> failwith $"readNodes failed: %A{x}"
reader p f
let private readObc (f: string array) =
let p =
function
| [| _; x; _ |] -> int x
| [| x; _ |] -> int x
| [| x |] -> int x
| _ -> failwith "readObc failed"
| [| _; x; _ |] -> int x - 1
| [| x; _ |] -> int x - 1
| [| x |] -> int x - 1
| _ -> failwith "readObc failed"
reader p f
let readGrdFile (filename: string) =
@@ -233,124 +285,446 @@ let readGrdFile (filename: string) =
|> Option.bind (fun (_, nele) ->
let els, nds = Array.splitAt nele rest
let elem = readElem els
let nodes = readNodes nds
let toGrid e n =
{
Grid.empty with
Elem = e
Nodes = n
BBox = calcBBox n
}
let nodes = readNodes nds
let toGrid e n = { Elem = e; Nodes = n; BBox = calcBBox n }
toGrid <!> elem <*> nodes
)
let readObcFile (filename: string) =
let f = System.IO.File.ReadAllLines filename
let hdr, rest = Array.splitAt 1 f
readObcHeader hdr[0]
|> Option.bind (fun _ -> readObc rest)
readObcHeader hdr[0] |> Option.bind (fun _ -> readObc rest)
type Edge = int * int
module Boundary =
let normalizeElement (a, b, c) =
[| a; b; c |]
|> Array.sort
|> fun x -> [| x[0], x[1]; x[1], x[2]; x[0], x[2] |]
let normalizeElement (a, b, c) =
[| a; b ; c |]
|> Array.sort
|> fun x ->
[|
x[0], x[1]
x[1], x[2]
x[0], x[2]
|]
let makeBoundaryByEdgeMap (grid: Grid) : Map<Edge, ElemIdx> =
let normElIdx n e = n, normalizeElement e
let appendEdge (n, edge: Edge) a =
match Map.tryFind edge a with
| Some v -> Map.add edge (n :: v) a
| None -> Map.add edge [ n ] a
let appendEdges a (n, x: Edge[]) =
a |> appendEdge (n, x[0]) |> appendEdge (n, x[1]) |> appendEdge (n, x[2])
grid.Elem
|> Array.mapi normElIdx
|> Array.fold appendEdges Map.empty
|> Map.filter (fun _ v -> v.Length = 1)
|> Map.mapValues List.head
let makeBoundaryByEdgeMap (grid: Grid) : Map<Edge, ElemIdx> =
let normElIdx n e = n, normalizeElement e
let appendEdge (n, edge: Edge) a =
match Map.tryFind edge a with
| Some v -> Map.add edge (n :: v) a
| None -> Map.add edge [ n ] a
let appendEdges a (n, x: Edge []) =
a
|> appendEdge (n, x[0])
|> appendEdge (n, x[1])
|> appendEdge (n, x[2])
grid.Elem
|> Array.mapi normElIdx
|> Array.fold appendEdges Map.empty
|> Map.filter (fun _ v -> v.Length = 1)
|> Map.mapValues List.head
let makeBoundaryByNodeMap (edges: Map<Edge, ElemIdx>) : Map<NodeIdx, ElemIdx * ElemIdx> =
let add node elem a =
match Map.tryFind node a with
| Some v -> Map.add node (elem :: v) a
| None -> Map.add node [ elem ] a
let appendNodes a (n1, n2) e = a |> add n1 e |> add n2 e
edges
|> Map.fold appendNodes Map.empty
|> Map.mapValues (fun x -> List.head x, List.last x)
let makeBoundaryByNodeMap (edges: Map<Edge, ElemIdx>) : Map<NodeIdx, ElemIdx * ElemIdx > =
let add node elem a =
match Map.tryFind node a with
| Some v -> Map.add node (elem :: v) a
| None -> Map.add node [ elem ] a
let appendNodes a (n1, n2) e =
a |> add n1 e |> add n2 e
edges
|> Map.fold appendNodes Map.empty
|> Map.mapValues (fun x -> List.head x, List.last x)
let makeBoundaryByElementMap (edgeMap: Map<Edge, ElemIdx>) =
edgeMap |> Map.fold (fun a k v -> Map.add v k a) Map.empty
let makeBoundaryByElementMap (edgeMap: Map<Edge, ElemIdx>) =
edgeMap |> Map.fold (fun a k v -> Map.add v k a ) Map.empty
let getBoundaryNodesArray (edgeMap: Map<Edge, ElemIdx>) =
edgeMap
|> Map.keys
|> Array.ofSeq
|> Array.collect (fun (a, b) -> [| a; b |])
|> Array.distinct
let getBoundaryNodesArray (edgeMap: Map<Edge, ElemIdx>) =
edgeMap
|> Map.keys
|> Array.ofSeq
|> Array.collect (fun (a ,b) -> [| a; b |])
|> Array.distinct
module Util =
// let private floatify = bimap float float
let calcElemCentroid (x0, y0) (x1, y1) (x2, y2) =
(x0 + x1 + x2) / 3.0, (y0 + y1 + y2) / 3.0
type Element =
static member calcCentroid((x0, y0), (x1, y1), (x2, y2)) =
(x0 + x1 + x2) / 3.0, (y0 + y1 + y2) / 3.0
let calcElemCentroid' (x0, y0) (x1, y1) (x2, y2) =
(x0 + x1 + x2) / 3f, (y0 + y1 + y2) / 3f
static member calcCentroid((x0, y0), (x1, y1), (x2, y2)) =
(x0 + x1 + x2) / 3f, (y0 + y1 + y2) / 3f
let calcElemArea (x0, y0) (x1, y1) (x2, y2) =
x0 * (y1 - y2) + x1 * (y2 - y0) + x2 * (y0 - y1) |> (*) 0.5
static member calcArea((x0, y0), (x1, y1), (x2, y2)) =
x0 * (y1 - y2) + x1 * (y2 - y0) + x2 * (y0 - y1) |> (*) 0.5
let private toF = bimap float float
// static member calcArea((x0, y0), (x1, y1), (x2, y2)) =
// x0 * (y1 - y2) + x1 * (y2 - y0) + x2 * (y0 - y1)
// |> (*) 0.5f
let calcNodeControlArea (idx: NeighborIndex) (grid: Grid) =
let half = (*) 0.5
[| 0 .. grid.Nodes.Length - 1 |]
|> Array.Parallel.map (fun n ->
getElemsSurroundingNode idx n
|> Array.map (fun e ->
let a, b, c = grid.Elem[e]
let x0, y0 as p0 = toF grid.Nodes[a]
let x1, y1 as p1 = toF grid.Nodes[b]
let x2, y2 as p2 = toF grid.Nodes[c]
let p1' = x0 + half (x1 - x0), y0 + half (y1 - y0)
let p2' = x0 + half (x2 - x0), y0 + half (y2 - y0)
let centroid = calcElemCentroid p0 p1 p2
let a1 = calcElemArea p0 p1' p2'
let a2 = calcElemArea p1' centroid p2'
a1 + a2
static member calcCircumscribedCircle((Ax, Ay as A), (Bx, By as B), (Cx, Cy as C)) =
let square (i, j) = i * i + j * j
let det (i0, i1, i2) (j0, j1, j2) (k0, k1, k2) =
i0 * (j1 * k2 - k1 * j2) - j0 * (i1 * k2 - k1 * i2) + k0 * (i1 * j2 - j1 * i2)
let T2 = square A, square B, square C
let Tx = Ax, Bx, Cx
let Ty = Ay, By, Cy
let E = 1.0, 1.0, 1.0
let Sx = det T2 Ty E |> (*) 0.5
let Sy = det Tx T2 E |> (*) 0.5
let a = det Tx Ty E
let b = det Tx Ty T2
if abs a < 1.0e-12 then
failwith "co-linear vertices"
let S2 = (Sx, Sy) |> square
let r = (b / a + S2 / (a * a)) |> sqrt
Sx / a, Sy / a, r
static member propToNodal (nIdx: NeighborIndex) (s: float[]) =
[| 0 .. nIdx.NodesAroundNode.Count - 1 |] // total number of nodes
|> Array.Parallel.map (fun i ->
let ns = nIdx.ElemsAroundNode[i]
ns |> Array.fold (fun a n -> s[n] + a) 0. |> (*) (1. / float ns.Length)
)
|> Array.sum
|> single
)
let calcNodeArea (idx: NeighborIndex) (grid: Grid) =
[| 0 .. grid.Nodes.Length - 1 |]
|> Array.Parallel.map (fun n ->
getElemsSurroundingNode idx n
|> Array.map (fun e ->
let a, b, c = grid.Elem[e]
let p0 = toF grid.Nodes[a]
let p1 = toF grid.Nodes[b]
let p2 = toF grid.Nodes[c]
calcElemArea p0 p1 p2)
|> Array.sum
|> single
)
static member speedToNodal (nIdx: NeighborIndex) (s: (float * float)[]) =
[| 0 .. nIdx.NodesAroundNode.Count - 1 |] // total number of nodes
|> Array.Parallel.map (fun i ->
let ns = nIdx.ElemsAroundNode[i]
ns
|> Array.fold
(fun a n ->
let u, v = s[n]
sqrt (u * u + v * v) + a
)
0.
|> (*) (1. / float ns.Length)
)
let genCells (grid: Grid)=
grid.Elem
|> Array.Parallel.map (fun (a, b, c) ->
let p0 = grid.Nodes[a]
let p1 = grid.Nodes[b]
let p2 = grid.Nodes[c]
calcElemCentroid' p0 p1 p2
static member velocityToNodal (nIdx: NeighborIndex) (s: (float * float)[]) =
[| 0 .. nIdx.NodesAroundNode.Count - 1 |] // total number of nodes
|> Array.Parallel.map (fun i ->
let ns = nIdx.ElemsAroundNode[i]
let n = float ns.Length
ns
|> Array.fold
(fun (au, av) n ->
let u, v = s[n]
u + au, v + av
)
(0., 0.)
|> fun (u, v) -> u / n, v / n
)
type Node =
static member calcNodeControlArea (idx: NeighborIndex) (grid: IGrid) =
let half = (*) 0.5
let nodes = grid.getVertices ()
[| 0 .. nodes.Length - 1 |]
|> Array.Parallel.map (fun n ->
getElemsSurroundingNode idx n
|> Array.map (fun e ->
let p0, p1, p2 = grid.getCellVertices e
let x0, y0 as p0' = p0
let x1, y1 = p1
let x2, y2 = p2
let p1' = x0 + half (x1 - x0), y0 + half (y1 - y0)
let p2' = x0 + half (x2 - x0), y0 + half (y2 - y0)
let centroid = Element.calcCentroid (p0, p1, p2)
let a1 = Element.calcArea (p0', p1', p2')
let a2 = Element.calcArea (p1', centroid, p2')
a1 + a2
)
|> Array.sum
)
static member calcNodeArea (idx: NeighborIndex) (grid: IGrid) =
let nodes = grid.getVertices ()
[| 0 .. nodes.Length - 1 |]
|> Array.Parallel.map (fun n ->
getElemsSurroundingNode idx n
|> Array.map (grid.getCellVertices >> Element.calcArea)
|> Array.sum
)
let calcCentroids (grid: IGrid) =
let n = grid.getVertices ()
grid.getCells ()
|> Array.Parallel.map (fun (a, b, c) ->
let p0 = n[a]
let p1 = n[b]
let p2 = n[c]
Element.calcCentroid (p0, p1, p2)
)
let inline isInsideTriangle (x, y, z) p =
let sign (p1x, p1y) (p2x, p2y) (p3x, p3y) =
(p1x - p3x) * (p2y - p3y) - (p2x - p3x) * (p1y - p3y)
let d1 = sign p x y
let d2 = sign p y z
let d3 = sign p z x
let neg = (d1 < 0.) || (d2 < 0.) || (d3 < 0.)
let pos = (d1 > 0.) || (d2 > 0.) || (d3 > 0.)
(neg && pos) |> not
// make a kd-tree for looking up nearest node
let buildNearestNodeTree (grid: IGrid) =
grid.getVertices ()
|> Array.mapi (fun i v ->
let x, y = v
{ Pos = x, y; Data = i }
)
// |> create2DTree treeLeafSize
|> createTree
// make a kd-tree for looking up nearest element
let buildNearestElementTree (grid: IGrid) =
grid.getCells ()
|> Array.mapi (fun i _ ->
let pos = i |> grid.getCellVertices |> Element.calcCentroid
// |> fun (x, y) -> { X = x; Y = y }
{ Pos = pos; Data = i }
)
// |> create2DTree treeLeafSize
|> createTree
// let tryFindElement
// (grid: IGrid)
// (tree: Tree<Leaf<single, ElemIdx> array, Node<single>>)
// ((p0, p1): single * single) =
// nearestNeighbor tree { X = p0; Y = p1 }
// |> Option.bind (fun leaf ->
// let vx = grid.getCellVertices leaf.Data
// if isInsideTriangle vx (p0, p1)
// then Some leaf.Data
// else None)
let tryFindElement (grid: IGrid) (tree: KdTree<float, int>) ((p0, p1): float * float) =
tree.GetNearestNeighbours ([| p0; p1 |], 1)
|> Array.tryHead
|> Option.bind (fun leaf ->
let vx = grid.getCellVertices leaf.Value
if isInsideTriangle vx (p0, p1) then
Some leaf.Value
else
None
)
// type private IdxTree = Tree<Leaf<single, ElemIdx> array, Node<single>>
// type private NodeIdxTree = Tree<Leaf<single, NodeIdx> array, Node<single>>
type private IdxTree = KdTree<float, int>
type private NodeIdxTree = KdTree<float, int>
[<MessagePackObject>]
type BinGrid = {
[<Key(0)>]
hash: byte[]
[<Key(1)>]
vertices: (float * float)[]
[<Key(2)>]
cells: (int * int * int)[]
} with
member this.toGrid() : Grid = { Nodes = this.vertices; Elem = this.cells; BBox = calcBBox this.vertices }
type ExtendedGrid(grid: IGrid) =
let mutable nodeTree: NodeIdxTree option = None
let mutable elementTree: IdxTree option = None
let mutable neighborIndex: NeighborIndex option = None
let mutable centroids: Vertex[] option = None
let mutable gridHash: byte[] = [||]
let getNeighborIdx () =
match neighborIndex with
| Some idx -> idx
| None ->
neighborIndex <- makeNeighborIndex grid |> Some
neighborIndex.Value
interface IGrid with
member this.getVertex n = grid.getVertex n
member this.getCell n = grid.getCell n
member this.getCellVertices n = grid.getCellVertices n
member this.getVertices() = grid.getVertices ()
member this.getCells() = grid.getCells ()
member this.getBoundingBox() = grid.getBoundingBox ()
member this.NeighborIndex = neighborIndex
member this.NodeTree = nodeTree
member this.ElementTree = elementTree
member this.Grid = grid
member this.ToGrid() = {
Nodes = this.Grid.getVertices ()
Elem = this.Grid.getCells ()
BBox = this.Grid.getBoundingBox ()
}
member this.initNeighborIndex(?cache: string) =
match neighborIndex with
| Some _ -> ()
| None -> neighborIndex <- makeNeighborIndex grid |> Some
member this.initNodeTree() =
match nodeTree with
| Some _ -> ()
| None -> nodeTree <- Util.buildNearestNodeTree grid |> Some
member this.initElementTree() =
match elementTree with
| Some _ -> ()
| None -> elementTree <- Util.buildNearestElementTree grid |> Some
member this.nearestNode(p0: float, p1: float) =
let nearest (tree: KdTree<_, _>) =
tree.GetNearestNeighbours ([| p0; p1 |], 1)
|> Array.tryHead
|> Option.map (fun l -> l.Value)
match nodeTree with
| Some tree -> nearest tree
| None ->
this.initNodeTree ()
nearest nodeTree.Value
member this.tryGetNode(p: float * float) =
this.nearestNode p
|> Option.bind (fun n ->
this.getElemsSurroundingNode n
|> Array.fold
(fun (a: int option) e ->
if a.IsNone then
let vx = this.Grid.getCellVertices e
if Util.isInsideTriangle vx p then Some n else None
else
a
)
None
)
member private this.tryFindElementTwice (grid: IGrid) (tree: IdxTree) ((p0, p1): float * float as p) =
Util.tryFindElement grid tree p
|> Option.orElse (
tree.GetNearestNeighbours ([| p0; p1 |], 1)
|> Array.tryHead
|> Option.bind (fun leaf ->
this.getElemsSurroundingElem leaf.Value
|> Array.tryFind (fun eIdx ->
let vx = this.Grid.getCellVertices eIdx
Util.isInsideTriangle vx p
)
)
)
member this.tryGetElement p =
elementTree
|> Option.bind (fun tree -> this.tryFindElementTwice grid tree p)
|> Option.orElseWith (fun () ->
this.initElementTree ()
this.tryFindElementTwice grid elementTree.Value p
)
member this.tryGetElementSloppy(p0, p1 as p) =
match elementTree with
| Some tree -> Util.tryFindElement grid tree p
| None ->
this.initElementTree ()
// this.initNodeTree ()
Util.tryFindElement grid elementTree.Value p
member this.initHash(hash: byte[]) =
if gridHash.Length = 0 && hash.Length > 0 then
gridHash <- hash
member this.getHash() =
if gridHash.Length = 0 then
let bg: BinGrid = {
hash = [||]
vertices = (this :> IGrid).getVertices ()
cells = (this :> IGrid).getCells ()
}
let bytes = MessagePackSerializer.Serialize (bg)
let sha1 = System.Security.Cryptography.SHA1.Create ()
gridHash <- sha1.ComputeHash bytes
gridHash
member this.getHashString() =
this.getHash () |> Convert.ToHexStringLower
member this.getCentroids() =
match centroids with
| Some cx -> cx
| None ->
let cx = Util.calcCentroids grid
centroids <- Some cx
cx
member this.calcCircumCircle e =
let triangle = grid.getCellVertices e
Util.Element.calcCircumscribedCircle triangle
member this.getElemsSurroundingNode n =
getNeighborIdx () |> fun idx -> idx.ElemsAroundNode[n]
member this.getNodesSurroundingNode n =
getNeighborIdx () |> fun idx -> idx.NodesAroundNode[n]
member this.getNodesSurroundingElem e =
let idx = getNeighborIdx ()
let elem = grid.getCell e
getSurrounding idx.NodesAroundNode elem
member this.getElemsSurroundingElem e =
let idx = getNeighborIdx ()
let elem = grid.getCell e
getSurrounding idx.ElemsAroundNode elem
member this.saveNeighborIndex(fname: string) =
let binarySerializer = FsPickler.CreateBinarySerializer ()
let nix = getNeighborIdx ()
let pickle = binarySerializer.Pickle nix
IO.File.WriteAllBytes (fname, pickle)
member this.loadNeighborIndex(fname: string) =
let binarySerializer = FsPickler.CreateBinarySerializer ()
if IO.File.Exists fname then
let pickle = IO.File.ReadAllBytes fname
neighborIndex <- binarySerializer.UnPickle<NeighborIndex> pickle |> Some
true
else
false
member this.saveNodeTree(fname: string) =
let binarySerializer = FsPickler.CreateBinarySerializer ()
let tree =
nodeTree
|> Option.defaultWith (fun () ->
this.initNodeTree ()
nodeTree.Value
)
let pickle = binarySerializer.Pickle tree
IO.File.WriteAllBytes (fname, pickle)
member this.loadNodeTree(fname: string) =
let binarySerializer = FsPickler.CreateBinarySerializer ()
if IO.File.Exists fname then
let pickle = IO.File.ReadAllBytes fname
nodeTree <- binarySerializer.UnPickle<IdxTree> pickle |> Some
true
else
false
member this.saveElementTree(fname: string) =
let binarySerializer = FsPickler.CreateBinarySerializer ()
let tree =
elementTree
|> Option.defaultWith (fun () ->
this.initElementTree ()
elementTree.Value
)
let pickle = binarySerializer.Pickle tree
IO.File.WriteAllBytes (fname, pickle)
member this.loadElementTree(fname: string) =
let binarySerializer = FsPickler.CreateBinarySerializer ()
if IO.File.Exists fname then
let pickle = IO.File.ReadAllBytes fname
elementTree <- binarySerializer.UnPickle<IdxTree> pickle |> Some
true
else
false

View File

@@ -1,17 +1,15 @@
module Oceanbox.FvcomKit.Interpol
open MathNet.Numerics.LinearAlgebra
open Serilog
open Types
open ROMS
open Grid
open Oceanbox.FvcomKit.Grid
open Adjoin
type InterpolCoefs = ((int * int) * (float * float)) array array
type DepthInterpolCoefs = { iRho: InterpolCoefs; iU: InterpolCoefs; iV: InterpolCoefs }
let private findNearestZ z0 (h: float []) =
let rec findNerest' z0 (h: float []) d n =
let private findNearestZ z0 (h: float[]) =
let rec findNerest' z0 (h: float[]) d n =
if n < h.Length - 1 then
let d' = abs (h[n] - z0)
if d' < d then findNerest' z0 h d' (n + 1) else n
@@ -23,9 +21,10 @@ let private calcInterpolationWeightedIdx (rn, fn) =
let nearestIdx = Array.map (fun fzi -> findNearestZ fzi rn) fn
nearestIdx
|> Array.mapi (fun i j ->
let last = Array.last rn
if fn[i] <= rn[0] then
(0, 0), (1.0, 0.0)
elif fn[i] >= rn[^0] then
elif fn[i] >= last then
(rn.Length - 1, 0), (1.0, 0.0)
elif abs (fn[i] - rn[j]) < 9.999999975e-07 then
(j, 0), (1.0, 0.0)
@@ -42,102 +41,170 @@ let private calcInterpolationWeightedIdx (rn, fn) =
else
failwith "not reachable")
let private calcInterpMatrices (rz: float [] []) (fz: float [] []) =
Array.zip rz fz
|> Array.Parallel.map calcInterpolationWeightedIdx
let private calcInterpMatrices (rz: float[][]) (fz: float[][]) =
Array.zip rz fz |> Array.Parallel.map calcInterpolationWeightedIdx
let mkDepthInterpolCoefs (siglay: single [,]) (h: float []) (roms: float [] []) =
let s =
siglay
|> Array2D.map float
|> Matrix.Build.DenseOfArray
let mkDepthInterpolCoefs (siglay: single[,]) (h: float[]) (roms: float[][]) =
let s = siglay |> Array2D.map float |> Matrix.Build.DenseOfArray
let rescale (sigma: float Matrix) h =
let sh = h |> Array.map float
sigma.MapIndexed(fun _ j x -> -x * sh[j])
|> fun x -> x.ToColumnArrays()
sigma.MapIndexed (fun _ j x -> -x * sh[j]) |> fun x -> x.ToColumnArrays ()
let conv m =
m
|> Array.map (Array.map ((*) -1.0))
|> matrix
|> fun x -> x.ToColumnArrays()
|> fun x -> x.ToColumnArrays ()
let romz = conv roms
let z = rescale s h
calcInterpMatrices romz z
// Linear interpolation to nearest vertical neighbor
let mkAllDepthInterpolCoefs (fvcom: Fvcom.FvcomGrid) (roms: AdjoinedGrid) =
Log.Information "Computing interpolation coefficients."
let uv =
Array.zip roms.u roms.v
|> Array.map (fun (u, v) -> (u + v) * 0.5)
{
iRho = mkDepthInterpolCoefs fvcom.Siglay roms.h roms.zRho
iU = mkDepthInterpolCoefs fvcom.SiglayCenter uv roms.zU
iV = mkDepthInterpolCoefs fvcom.SiglayCenter uv roms.zV
}
let zInterpolProp (iz: InterpolCoefs) (adjRomsProp: float [] []) =
let zInterpolProp (iz: InterpolCoefs) (adjRomsProp: float[][]) =
let pz = adjRomsProp |> matrix
iz
|> Array.Parallel.mapi (fun n x ->
let p = pz[ *, n ].ToArray() |> Array.rev
x
|> Array.map (fun ((i1, i2), (w1, w2)) -> p[i1] * w1 + p[i2] * w2))
let p = pz[*, n].ToArray () |> Array.rev
x |> Array.map (fun ((i1, i2), (w1, w2)) -> p[i1] * w1 + p[i2] * w2))
|> matrix
|> fun x -> x.Transpose().ToArray()
|> fun x -> x.Transpose().ToArray ()
// Trivially already satisfied by ajoint
let hInterpolNearestProp (adjRomsProp: float [] []) = adjRomsProp
let hInterpolNearestProp (adjRomsProp: float[][]) = adjRomsProp
let interpolateFromRoms doUv (fvGrid: Fvcom.FvcomGrid) rGrid rProps =
let grid = fvGrid.ToGrid()
let aGrid, aProps = adjoinRomsToFvcom grid rGrid rProps
let ic = mkAllDepthInterpolCoefs fvGrid aGrid
Log.Information "Interpolating props."
let fvData: Fvcom.FvcomRestart =
{
salinity = zInterpolProp ic.iRho aProps.salt |> propTo3D
temp = zInterpolProp ic.iRho aProps.temp |> propTo3D
zeta = aProps.zeta |> propTo2D
u = Array3D.zeroCreate 0 0 0
v = Array3D.zeroCreate 0 0 0
ua = Array2D.zeroCreate 0 0
va = Array2D.zeroCreate 0 0
}
if doUv then
Log.Information "Interpolating u and v."
let angles =
computeAngles fvGrid aGrid
|> Vector.Build.DenseOfArray
let cosA = Vector.Cos angles
let sinA = Vector.Sin angles
let u =
zInterpolProp ic.iU aProps.u
|> Matrix.Build.DenseOfArray
let v =
zInterpolProp ic.iV aProps.v
|> Matrix.Build.DenseOfArray
let k =
[|
for i = 0 to u.RowCount - 1 do
let u' = u.Row i
let v' = v.Row i
u'.PointwiseMultiply cosA
- v'.PointwiseMultiply sinA,
u'.PointwiseMultiply sinA
+ v'.PointwiseMultiply cosA
|]
Log.Information "Computing ubar and vbar."
let u' = k |> Array.map (fun (u, _) -> u.ToArray())
let v' = k |> Array.map (fun (_, v) -> v.ToArray())
let ubar, vbar = calcUVBar fvGrid (u', v')
{ fvData with
u = u' |> matrix |> fun x -> x.ToArray() |> propTo3D
v = v' |> matrix |> fun x -> x.ToArray() |> propTo3D
ua = ubar.ToArray() |> propTo2D
va = vbar.ToArray() |> propTo2D
}
type BiWght = float * float * float * float
type BiProp = float * float * float * float
type NW'SE = (float * float) * (float * float)
let genBilinearInterpolationWgts
(x: float, y: float)
((x0, y0), (x1, y1), (x2, y2), (x3, y3))
((mask0, mask1, mask2, mask3): bool * bool * bool * bool)
: BiWght option =
if mask0 && mask1 && mask2 && mask3 then
let AB = (x1 - x0) ** 2.0 + (y1 - y0) ** 2.0 |> sqrt
let BC = (x2 - x1) ** 2.0 + (y2 - y1) ** 2.0 |> sqrt
let CD = (x3 - x2) ** 2.0 + (y3 - y2) ** 2.0 |> sqrt
let DA = (x0 - x3) ** 2.0 + (y0 - y3) ** 2.0 |> sqrt
let h0 =
let a0 = (x * (y0 - y3) + x0 * (y3 - y) + x3 * (y - y0)) * 0.5
2.0 * a0 / DA
let h1 =
let a1 = (x * (y1 - y0) + x0 * (y - y1) + x1 * (y0 - y)) * 0.5
2.0 * a1 / AB
let w00 = (h0 / AB - 1.0) * (h1 / DA - 1.0)
let w10 = h0 / AB * (1.0 - h1 / DA)
let w11 = h1 / DA * h0 / CD
let w01 = h1 / DA * (1.0 - h0 / CD)
Some (w00, w10, w11, w01)
else
fvData
None
let makeBiWeights
(pos: (float * float)[])
(boxes: ((float * float) * (float * float) * (float * float) * (float * float))[])
(mask: (bool * bool * bool * bool)[])
=
Array.zip3 pos boxes mask
|> Array.map (fun (p, b, m) -> genBilinearInterpolationWgts p b m)
let interpolateCells (wghts: BiWght option[]) (prop: BiProp[]) =
Array.zip wghts prop
|> Array.Parallel.map (fun (w, (p00, p01, p11, p10)) ->
match w with
| Some w ->
let w00, w01, w11, w10 = w
Some (w00 * p00 + w01 * p01 + w11 * p11 + w10 * p10)
| None -> None)
// |> Array.Parallel.map (fun ((w00, w01, w11, w10), (p00, p01, p11, p10)) ->
// w00 * p00 + w01 * p01 + w11 * p11 + w10 * p10)
let private valtest (intval: float option[]) =
let somval = intval |> Array.filter (fun v -> Option.isSome v)
somval.Length = intval.Length
let private fillOutOfBounds (grid: Grid) (interpVal: float option[]) =
let node = interpVal.Length = grid.Nodes.Length
let nbridx = makeNeighborIndex (grid :> IGrid)
let mutable allval = valtest interpVal
while not allval do
for n = 0 to interpVal.Length - 1 do
if Option.isNone interpVal[n] then
let nb =
if node then
nbridx.NodesAroundNode[n] |> Array.filter (fun k -> Option.isSome interpVal[k])
else
getElemsSurroundingElem nbridx grid n
|> Array.filter (fun k -> Option.isSome interpVal[k])
if nb.Length > 0 then
let nval = nb |> Array.map (fun k -> interpVal[k].Value) |> Array.average
interpVal[n] <- Some nval
allval <- valtest interpVal
interpVal |> Array.map (fun v -> v.Value)
let interpolateProp
(coords, _ as grid: BiPos * Mask)
(pos: (float * float)[])
(prop: float[,])
(fvgrid: Grid)
(oob: bool)
=
let cm = getNearestCellCorner grid pos
let boxes, mask =
cm
|> Array.Parallel.map (fun box -> getCellBox coords box)
|> Array.map (fun (coord, m) -> (coord[0], coord[1], coord[2], coord[3]), (m[0], m[1], m[2], m[3]))
|> Array.unzip
let wghts = makeBiWeights pos boxes mask
let boxProps =
cm
|> Array.map (fun box -> box |> Array.map (fun ((n, m), _) -> prop[n, m]))
|> Array.map (fun p -> (p[0], p[1], p[2], p[3]))
let iprop = interpolateCells wghts boxProps
if not (valtest iprop) && (not oob) then
let sidx =
iprop
|> Array.mapi (fun id p ->
match p with
| Some _ -> -1
| None -> id)
|> Array.filter (fun i -> i > 0)
|> Array.fold (fun a i -> a + "; " + string i) ""
failwith ($"FVCOM grid out of bounds. Oob indexes: {sidx}")
if oob then
fillOutOfBounds fvgrid iprop
else
iprop |> Array.map (fun v -> v.Value)
let interpCoefs (coords, _ as grid: BiPos * Mask) (pos: (float * float)[]) =
let cm = getNearestCellCorner grid pos
let boxes, mask =
cm
|> Array.Parallel.map (fun box -> getCellBox coords box)
|> Array.map (fun (coord, m) -> (coord[0], coord[1], coord[2], coord[3]), (m[0], m[1], m[2], m[3]))
|> Array.unzip
let wgths = makeBiWeights pos boxes mask
let corneridx =
cm |> Array.map (fun c -> Array.unzip c) |> Array.map (fun (i, _) -> i)
corneridx, wgths
let interpProp (corneridx: (int * int)[][]) (wgths: BiWght option[]) (prop: float[,]) (grid: Grid) (oob: bool) =
let boxProps =
corneridx
|> Array.map (fun box -> box |> Array.map (fun (n, m) -> prop[n, m]))
|> Array.map (fun p -> (p[0], p[1], p[2], p[3]))
let iprop = interpolateCells wgths boxProps
if not (valtest iprop) && (not oob) then
let sidx =
iprop
|> Array.mapi (fun i p ->
match p with
| Some _ -> -1
| None -> i)
|> Array.filter (fun i -> i >= 0)
|> Array.fold (fun a i -> a + "; " + string i) ""
failwith ($"FVCOM grid out of bounds. Oob indexes: {sidx}")
if oob then
fillOutOfBounds grid iprop
else
iprop |> Array.map (fun v -> v.Value)

View File

@@ -1,18 +1,21 @@
module Oceanbox.FvcomKit.NorKyst
open System
open Thredds
let private getNorkystUrl (threddsUrl: string) (d: DateTime) =
let url = threddsUrl
let fmt (d: DateTime) =
$"{d.Year}%02d{d.Month}%02d{d.Day}T00Z.nc"
$"{url}/fou-hi/new_norkyst800m/his/ocean_his.an.{fmt d}"
let private getArchiveUrls urlf (t: DateTime) =
let t = t.ToUniversalTime()
let now = DateTime.Now.ToUniversalTime()
let t = t.ToUniversalTime ()
let now = DateTime.Now.ToUniversalTime ()
let dDay = (now.Date - t.Date).Days
if dDay < 0 then // no data available
[]
else
@@ -21,6 +24,4 @@ let private getArchiveUrls urlf (t: DateTime) =
let tryGetArchive (threddsUrl: string) (t: DateTime) =
getArchiveUrls (getNorkystUrl threddsUrl) t
|> tryOpenThredds
|> Option.bind (fun (_, ds) ->
tryGetTimeIndex ds t
|> Option.map (fun idx -> (ds, idx)))
|> Option.bind (fun (_, ds) -> tryGetTimeIndex ds t |> Option.map (fun idx -> ds, idx))

View File

@@ -10,12 +10,14 @@ let private getNorshelfUrl (threddsUrl: string) (kind: Kind) (mode: Mode) (date:
let fmt (d: DateTime) =
$"{d.Year}%02d{d.Month}%02d{d.Day}T00Z.nc"
let url = threddsUrl + "/sea_norshelf_files"
$"{url}/norshelf_{kind}_{mode}_{fmt date}"
$"{url}/{date.Year}/%02d{date.Month}/norshelf_{kind}_{mode}_{fmt date}"
let private getArchiveUrls urlf (t: DateTime) =
let t = t.ToUniversalTime()
let now = DateTime.Now.ToUniversalTime()
let t = t.ToUniversalTime ()
let now = DateTime.Now.ToUniversalTime ()
let dDay = (now.Date - t.Date).Days
if dDay < -3 then // no data available
[]
elif dDay <= 0 then // forecast, count down from latest
@@ -27,11 +29,10 @@ let private getArchiveUrls urlf (t: DateTime) =
let tryGetArchive threddsUrl avg (t: DateTime) =
let kind = if avg then Avg else Qck
getArchiveUrls (getNorshelfUrl threddsUrl kind) t
|> tryOpenThredds
|> Option.bind (fun (fname, ds) ->
tryGetTimeIndex ds t
|> Option.map (fun idx -> (fname, ds, idx)))
|> Option.bind (fun (fname, ds) -> tryGetTimeIndex ds t |> Option.map (fun idx -> (fname, ds, idx)))
let readArchive file reader =
tryOpenArchive file
@@ -40,12 +41,11 @@ let readArchive file reader =
let data = reader nc
Log.Information $"Read NorShelf data from {file}"
Some data
with
| e ->
with e ->
Log.Error e.Message
None)
let readRomsGrid fname = readArchive fname readGrid
let readRomsProps fname idx =
readArchive fname ((flip readProps) idx)
readArchive fname ((flip readProps) idx)

View File

@@ -1,38 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk.Web">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>net6.0</TargetFramework>
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
<IsPackable>true</IsPackable>
<PackageId>Oceanbox.FvcomKit</PackageId>
<TargetFramework>net9.0</TargetFramework>
<Company>Oceanbox AS</Company>
<Authors/>
<Company/>
<Version>2.1.0</Version>
<LangVersion>preview</LangVersion>
<PackageId>Oceanbox.FvcomKit</PackageId>
<RuntimeIdentifier>linux-x64</RuntimeIdentifier>
<Version>6.0.0</Version>
<RestorePackagesWithLockFile>false</RestorePackagesWithLockFile>
<IsPackable>true</IsPackable>
</PropertyGroup>
<ItemGroup>
<Compile Include="Types.fs"/>
<Compile Include="Grid.fs"/>
<Compile Include="Arome.fs"/>
<Compile Include="Fvcom.fs"/>
<Compile Include="Thredds.fs"/>
<Compile Include="ROMS.fs"/>
<Compile Include="Polygon.fs"/>
<Compile Include="Adjoin.fs"/>
<Compile Include="Interpol.fs"/>
<Compile Include="ROMS.fs"/>
<Compile Include="NorKyst.fs"/>
<Compile Include="NorShelf.fs"/>
<Compile Include="Smoothing.fs"/>
</ItemGroup>
<ItemGroup>
<PackageReference Update="FSharp.Core" Version="6.0.4-beta.22181.2"/>
<PackageReference Include="FSharp.Data" Version="4.2.8"/>
<PackageReference Include="FSharpPlus" Version="1.2.3.1-PR476-02311"/>
<PackageReference Include="FSharp.Data" Version="6.4.1"/>
<PackageReference Include="FSharpPlus" Version="1.7.0"/>
<PackageReference Include="FsPickler" Version="5.3.2"/>
<PackageReference Include="KDTree" Version="1.4.1"/>
<PackageReference Include="MathNet.Numerics.FSharp" Version="5.0.0"/>
<PackageReference Include="ProjNet.FSharp" Version="3.0.1"/>
<PackageReference Include="sdslite" Version="2.3.0"/>
<PackageReference Include="Serilog" Version="2.11.0"/>
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.2-dev-00890"/>
<PackageReference Include="Serilog.Sinks.Seq" Version="5.1.2-dev-00222"/>
<PackageReference Include="Thoth.Json.Net" Version="8.0.0"/>
<PackageReference Include="MessagePack" Version="3.1.3"/>
<PackageReference Include="ProjNet.FSharp" Version="5.2.0"/>
<PackageReference Include="Oceanbox.SDSLite" Version="2.8.0"/>
<PackageReference Include="Serilog" Version="4.2.0"/>
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0"/>
<PackageReference Include="Serilog.Sinks.Seq" Version="9.0.0"/>
<PackageReference Update="FSharp.Core" Version="9.0.303"/>
</ItemGroup>
<ItemGroup>
<PackageUpdate Include="ProjNet.FSharp" Version="*" Condition=" '$(ContinuousIntegrationBuild)'=='true' "/>
<PackageUpdate Include="Oceanbox.SDSLite" Version="*" Condition=" '$(ContinuousIntegrationBuild)'=='true' "/>
</ItemGroup>
</Project>

66
src/Polygon.fs Normal file
View File

@@ -0,0 +1,66 @@
module Polygon
type Point = float * float
type Polygon = Point[]
let private maxdist (pol: Polygon) (pnt: Point) =
pol
|> Array.map (fun (x, y) -> sqrt ((x - fst pnt) ** 2.0 + (y - snd pnt) ** 2.0))
|> Array.max
let private orientation (pol3: Polygon) =
let diff seg =
let px = seg |> Array.pairwise
px |> Array.map (fun (x1, x2) -> x2 - x1)
let x, y = pol3 |> Array.unzip
let dx, dy = diff x, diff y
let ori = dy[0] * dx[1] - dy[1] * dx[0]
if ori > 0.0 then 1
elif ori < 0.0 then 2
else 0
let private onsegment (p: Polygon) =
let x, y = p |> Array.unzip
let maxx = x[0..1] |> Array.max
let minx = x[0..1] |> Array.min
let maxy = y[0..1] |> Array.max
let miny = y[0..1] |> Array.min
if x[2] >= minx && x[2] <= maxx && y[2] >= miny && y[2] <= maxy then
true
else
false
let private intersect (p1: Polygon) (p2: Polygon) =
let x1, y1 = p1 |> Array.unzip
let x2, y2 = p2 |> Array.unzip
let pol1 = [| x1[0], y1[0]; x1[1], y1[1]; x2[0], y2[0] |]
let pol2 = [| x1[0], y1[0]; x1[1], y1[1]; x2[1], y2[1] |]
let pol3 = [| x2[0], y2[0]; x2[1], y2[1]; x1[0], y1[0] |]
let pol4 = [| x2[0], y2[0]; x2[1], y2[1]; x1[1], y1[1] |]
let o1, o2, o3, o4 =
orientation pol1, orientation pol2, orientation pol3, orientation pol4
if o1 <> o2 && o3 <> o4 then true
elif o1 = 0 && onsegment pol1 then true
elif o2 = 0 && onsegment pol2 then true
elif o3 = 0 && onsegment pol3 then true
elif o4 = 0 && onsegment pol4 then true
else false
let private isodd a = if a % 2 = 0 then false else true
let inpolygon (pol: Polygon) (point: Point) =
let (line: Polygon) = [| fst point, snd point; fst point + (maxdist pol point), snd point |]
let poly =
if pol[0] <> pol[pol.Length - 1] then
Array.append pol [| pol[0] |] |> Array.pairwise
else
pol |> Array.pairwise
poly
|> Array.map (fun (p1, p2) -> intersect line [| p1; p2 |])
|> Array.map (fun x -> if x then 1 else 0)
|> Array.sum
|> isodd

View File

@@ -1,118 +1,90 @@
module Oceanbox.FvcomKit.ROMS
open System.Diagnostics
open FSharpPlus
open Serilog
open Microsoft.Research.Science.Data
open MathNet.Numerics.LinearAlgebra
open KdTree
open ProjNet.FSharp
open Grid
open Types
open Adjoin
open Interpol
type GridData<'a> = { rho: 'a; u: 'a; v: 'a }
type private CropBox = (float * float) * (float * float)
type private Mask = bool [,]
type private PosVec = (float * float) []
type private RomsPos = float [,] * float [,]
type GridMask = GridData<bool [,]>
type GridPos = GridData<RomsPos>
type GridVars = GridData<float [,]>
type GridMask = GridData<bool[,]>
type GridPos = GridData<BiPos>
type GridVars = GridData<float[,]>
type RomsGrid =
{
h: GridVars
z: GridVars []
pos: GridPos
wetMask: GridMask
angle: float [,]
}
type RomsGrid = {
h: GridVars
z: GridVars[]
pos: GridPos
wetMask: GridMask
angle: float[,]
}
type RomsProps =
{
salt: float [,] []
temp: float [,] []
zeta: float [,]
u: float [,] []
v: float [,] []
}
type RomsProps = {
salt: float[,][]
temp: float[,][]
zeta: float[,]
u: float[,][]
v: float[,][]
}
type AdjoinedProp =
{
salt: float [] []
temp: float [] []
zeta: float []
u: float [] []
v: float [] []
}
type AdjoinedProp = {
salt: float[][]
temp: float[][]
zeta: float[]
u: float[][]
v: float[][]
}
type FvcomGrid = Fvcom.FvcomGrid
let private lngLatToUtm33 = ProjNet.FSharp.WGS84_TO_UTM 33
let private toUtm33 ((lng, lat): float [,] * float [,]) =
lng
|> Array2D.mapi (fun i j lon -> lon, lat[i, j])
|> Array2D.map lngLatToUtm33.project
|> unzip2D
let private layerZ csR (vars: GridVars) =
let mult m x = Array2D.map ((*) x) m
let rho = csR |> Array.map (mult vars.rho)
let u = csR |> Array.map (mult vars.u)
let v = csR |> Array.map (mult vars.v)
Array.zip3 rho u v
|> Array.map (fun (a, b, c) -> { rho = a; u = b; v = c })
Array.zip3 rho u v |> Array.map (fun (a, b, c) -> { rho = a; u = b; v = c })
// convert fractional ROMS landmask to ocean mask
let readWetMask (nc: DataSet) =
let isWet = Array2D.map ((=) 1.)
{
rho = nc[ "mask_rho" ].GetData() :?> float [,] |> isWet
u = nc[ "mask_u" ].GetData() :?> float [,] |> isWet
v = nc[ "mask_v" ].GetData() :?> float [,] |> isWet
rho = nc["mask_rho"].GetData () :?> float[,] |> isWet
u = nc["mask_u"].GetData () :?> float[,] |> isWet
v = nc["mask_v"].GetData () :?> float[,] |> isWet
}
let private readProp3 (nc: DataSet) (p: string) t (prop: string) =
let nEta = nc.Dimensions[$"eta_{p}"].Length
let nXi = nc.Dimensions[$"xi_{p}"].Length
let scaling =
nc.Variables[prop].Metadata["scale_factor"] :?> single
|> float
let offset =
nc.Variables[prop].Metadata["add_offset"] :?> single
|> float
let scaling = nc.Variables[prop].Metadata["scale_factor"] :?> single |> float
let offset = nc.Variables[prop].Metadata["add_offset"] :?> single |> float
let o = [| t; 0; 0 |]
let shp = [| 1; nEta; nXi |]
let convert (x: int16 [,,]) =
x[0, *, *]
|> Array2D.map (fun x -> float x * scaling + offset)
nc[ prop ].GetData(o, shp) :?> int16 [,,]
|> convert
let convert (x: int16[,,]) =
x[0, *, *] |> Array2D.map (fun x -> float x * scaling + offset)
nc[prop].GetData (o, shp) :?> int16[,,] |> convert
let private readProp4 (nc: DataSet) (p: string) t (prop: string) =
let nEta = nc.Dimensions[$"eta_{p}"].Length
let nXi = nc.Dimensions[$"xi_{p}"].Length
let nS = nc.Dimensions["s_rho"].Length
let scaling =
nc.Variables[prop].Metadata["scale_factor"] :?> single
|> float
let offset =
nc.Variables[prop].Metadata["add_offset"] :?> single
|> float
let scaling = nc.Variables[prop].Metadata["scale_factor"] :?> single |> float
let offset = nc.Variables[prop].Metadata["add_offset"] :?> single |> float
let o = [| t; 0; 0; 0 |]
let shp = [| 1; nS; nEta; nXi |]
let convert (x: int16 [,,,]) =
let convert (x: int16[,,,]) =
x[0, *, *, *]
|> Array3D.map (fun x -> float x * scaling + offset)
|> fun m ->
[|
for z = 0 to nS - 1 do
m[z, *, *]
|]
|> fun m -> [|
for z = 0 to nS - 1 do
m[z, *, *]
|]
nc[ prop ].GetData(o, shp) :?> int16 [,,,]
|> convert
nc[prop].GetData (o, shp) :?> int16[,,,] |> convert
let readProps (nc: DataSet) t : RomsProps =
Log.Information "Reading ROMS props..."
@@ -131,177 +103,58 @@ let readVerticalGrid path =
match Thredds.tryOpenArchive path with
| Some ds -> ds
| None -> failwith "open vertical grid failed"
nc[ "Cs_r" ].GetData() :?> float [] |> Array.rev
nc["s_rho"].GetData () :?> float[] |> Array.rev
let readGrid (nc: DataSet) =
Log.Information "Reading ROMS grid..."
let hRho =
nc[ "h" ].GetData() :?> float [,]
|> Matrix.Build.DenseOfArray
let hRho = nc["h"].GetData () :?> float[,] |> Matrix.Build.DenseOfArray
let csR = nc[ "Cs_r" ].GetData() :?> float [] |> Array.rev
let srho = nc["s_rho"].GetData () :?> float[] |> Array.rev
let e1, e2 = hRho.ColumnCount - 2, hRho.RowCount - 2
let hU = (hRho[*, 1..] + hRho[*, ..e1]) / 2.
let hV = (hRho[1.., *] + hRho[..e2, *]) / 2.
let pos: GridPos =
{
rho = nc[ "lon_rho" ].GetData() :?> float [,], nc[ "lat_rho" ].GetData() :?> float [,]
u = nc[ "lon_u" ].GetData() :?> float [,], nc[ "lat_u" ].GetData() :?> float [,]
v = nc[ "lon_v" ].GetData() :?> float [,], nc[ "lat_v" ].GetData() :?> float [,]
}
let angle = nc[ "angle" ].GetData() :?> float [,]
let h = { rho = hRho.ToArray(); u = hU.ToArray(); v = hV.ToArray() }
let pos: GridPos = {
rho = nc["lon_rho"].GetData () :?> float[,], nc["lat_rho"].GetData () :?> float[,]
u = nc["lon_u"].GetData () :?> float[,], nc["lat_u"].GetData () :?> float[,]
v = nc["lon_v"].GetData () :?> float[,], nc["lat_v"].GetData () :?> float[,]
}
let angle = nc["angle"].GetData () :?> float[,]
let h = { rho = hRho.ToArray (); u = hU.ToArray (); v = hV.ToArray () }
{
h = h
z = layerZ csR h
z = layerZ srho h
pos = pos
wetMask = readWetMask nc
angle = angle
}
let private genCropIdx ((min, max): float * float) (m: float [,]) =
[|
for i = 0 to Array2D.length1 m - 1 do
for j = 0 to Array2D.length2 m - 1 do
if m[i, j] >= min && m[i, j] <= max then i, j else ()
|]
|> Set.ofArray
let private genCropMask ((xlim, ylim): CropBox) ((x, y): RomsPos) =
let ind1 = genCropIdx xlim x
let ind2 = genCropIdx ylim y
Set.intersect ind1 ind2 |> Set.toArray
// compute a padded fvcom bounding box for cropping roms grids
let private mkCropBox (box: BBox) : CropBox =
let pad min max =
let d = 5000.0
float min - d, float max + d
// let p = toUtm33 roms
let xlim = pad box.minX box.maxX
let ylim = pad box.minY box.maxY
xlim, ylim
// make an array of indeces of wet roms grid points inside the fvcom domain
let private genCullIdx (box: CropBox) (wet: Mask) (p: RomsPos) =
genCropMask box p
|> Array.filter (fun (i, j) -> wet[i, j])
// crop roms grid coordinates based in index mask of active, overlapping points
let private cullCoords (p: RomsPos) (idx: (int * int) []) =
let x, y = p
let x' = idx |> Array.map (fun (i, j) -> x[i, j])
let y' = idx |> Array.map (fun (i, j) -> y[i, j])
Array.zip x' y'
// make a kd-tree for fast nearest neighbour lookup
let private buildTree (points: (float * float) array) =
let tree = KdTree<float, int>(2, KdTree.Math.DoubleMath())
points
|> Array.iteri (fun n (x, y) -> tree.Add([| x; y |], n) |> ignore)
tree.Balance()
tree
// adjoinIdx: index to culled roms grid for each fvcom node
// cullIdx: index to roms points _actually_ in use
// oobIdx: out-of-bounds points, too far from a roms cell
type FvcomAdjoint = { adjoinIdx: int []; cullIdx: (int * int) []; oobIdx: int [] }
let private dist (a: float * float) (b: float * float) =
let x0, y0 = a
let x1, y1 = b
let dX = x1 - x0
let dY = y1 - y0
dX ** 2 + dY ** 2 |> sqrt |> (*) 0.5
let private distLngLat (a: float * float) (b: float * float) =
let x0, y0 = a |> lngLatToUtm33.project
let x1, y1 = b |> lngLatToUtm33.project
let dX = x1 - x0
let dY = y1 - y0
dX ** 2 + dY ** 2 |> sqrt |> (*) 0.5
let private genOobIdx (tree: KdTree<float, int>) (cullIdx: (int * int) []) (fPos: PosVec) (rPos: RomsPos) =
let rLon, rLat = rPos
let dMax = distLngLat (rLon[0, 0], rLat[0, 0]) (rLon[0, 1], rLat[0, 1])
fPos
|> Array.fold
(fun (n, a) (x, y as p0) ->
tree.GetNearestNeighbours([| x; y |], 1)
|> fun p ->
let i0, i1 = cullIdx[p[0].Value]
let p1 = lngLatToUtm33.project ((rLon[i0, i1], rLat[i0, i1]))
let d = dist p0 p1
if d > dMax then n + 1, n :: a else n + 1, a)
(0, [])
|> snd
|> Array.ofList
// compute the nearest neighbour index into the cropped roms grid
let private mkFvcomAdjoint (fPos: PosVec) (bbox: BBox) ((rPos, wet): RomsPos * Mask) =
let box = mkCropBox bbox
let pos' = toUtm33 rPos
let cullIdx = genCullIdx box wet pos'
let tree = cullCoords pos' cullIdx |> buildTree
let nearest =
fPos
|> Array.map (fun (x, y) ->
tree.GetNearestNeighbours([| x; y |], 1)
|> fun x -> x[0].Value)
{
adjoinIdx = nearest
cullIdx = cullIdx
oobIdx = genOobIdx tree cullIdx fPos rPos
}
let private getNearestNode (fvcom: Grid) (roms: RomsPos * Mask) =
let fvNodes = Array.map (fun (x, y) -> float x, float y) fvcom.Nodes
mkFvcomAdjoint fvNodes fvcom.BBox roms
// same as getNearestNode, but for uv in the center of a cell/element
let private getNearestCell (fvcom: Grid) (roms: RomsPos * Mask) =
let cells = genCells fvcom |> Array.map (bimap float float)
mkFvcomAdjoint cells fvcom.BBox roms
// pick out elements actually in use
let inline private cullRomsMatrix (culler: (int * int) []) (m: 'a [,]) =
culler |> Array.map (fun (i, j) -> m[i, j])
// adjoin fvcom and roms data based on nearest neighbours
let private adjoinMatrix (adj: FvcomAdjoint) (m: 'a [,]) =
let x = cullRomsMatrix adj.cullIdx m
adj.adjoinIdx |> Array.map (fun n -> x[n])
type IRomsToFvcom =
abstract uv: bool
abstract rhoToFvcom: 'a [,] -> 'a []
abstract uToFvcom: 'a [,] -> 'a []
abstract vToFvcom: 'a [,] -> 'a []
abstract rhoToFvcom: 'a[,] -> 'a[]
abstract uToFvcom: 'a[,] -> 'a[]
abstract vToFvcom: 'a[,] -> 'a[]
abstract rhoAdjoint: FvcomAdjoint
abstract uAdjoint: FvcomAdjoint
abstract vAdjoint: FvcomAdjoint
type AdjoinedGrid =
{
h: float []
zRho: float [] []
zU: float [] []
zV: float [] []
angle: float []
u: float []
v: float []
}
type AdjoinedGrid = {
h: float[]
zRho: float[][]
zU: float[][]
zV: float[][]
angle: float[]
u: float[]
v: float[]
}
let adjoinGirds (r2f: IRomsToFvcom) (roms: RomsGrid) =
Log.Information "Adjoining grids."
{
h = r2f.rhoToFvcom roms.h.rho
zRho =
roms.z
|> Array.map (fun h -> r2f.rhoToFvcom h.rho)
zRho = roms.z |> Array.map (fun h -> r2f.rhoToFvcom h.rho)
zU = roms.z |> Array.map (fun h -> r2f.uToFvcom h.u)
zV = roms.z |> Array.map (fun h -> r2f.vToFvcom h.v)
angle = r2f.rhoToFvcom roms.angle
@@ -319,30 +172,29 @@ let adjoinProps (r2f: IRomsToFvcom) (props: RomsProps) =
v = Array.map r2f.vToFvcom props.v
}
let genRomsToFvcomAdjoints (fvcom: Grid) (roms: RomsGrid) =
let rhoM = getNearestNode fvcom (roms.pos.rho, roms.wetMask.rho)
let genRomsToFvcomAdjoints (proj: IProj) (fvcom: Grid) (roms: RomsGrid) =
let rhoM = getNearestNode proj fvcom (roms.pos.rho, roms.wetMask.rho)
let uvMappings =
let uM = getNearestCell fvcom (roms.pos.u, roms.wetMask.u)
let vM = getNearestCell fvcom (roms.pos.v, roms.wetMask.v)
let uM = getNearestCell proj fvcom (roms.pos.u, roms.wetMask.u)
let vM = getNearestCell proj fvcom (roms.pos.v, roms.wetMask.v)
uM, vM
{ new IRomsToFvcom with
member x.uv = true
member x.rhoToFvcom m = adjoinMatrix rhoM m
member x.uToFvcom m = adjoinMatrix (fst uvMappings) m
member x.vToFvcom m = adjoinMatrix (snd uvMappings) m
member x.rhoAdjoint = rhoM
member x.uAdjoint = fst uvMappings
member x.vAdjoint = snd uvMappings
member this.uv = true
member this.rhoToFvcom m = adjoinMatrix rhoM m
member this.uToFvcom m = adjoinMatrix (fst uvMappings) m
member this.vToFvcom m = adjoinMatrix (snd uvMappings) m
member this.rhoAdjoint = rhoM
member this.uAdjoint = fst uvMappings
member this.vAdjoint = snd uvMappings
}
let adjoinRomsToFvcom (fvcom: Grid) (roms: RomsGrid) (props: RomsProps) =
let r2f = genRomsToFvcomAdjoints fvcom roms
let adjoinRomsToFvcom (proj: IProj) (fvcom: Grid) (roms: RomsGrid) (props: RomsProps) =
let r2f = genRomsToFvcomAdjoints proj fvcom roms
adjoinGirds r2f roms, adjoinProps r2f props
let computeAngles (grid: FvcomGrid) (adj: AdjoinedGrid) =
let a = adj.angle
grid.Elem
|> Array.map (fun (i, j, k) -> (a[i] + a[j] + a[k]) / 3.0)
grid.Elem |> Array.map (fun (i, j, k) -> (a[i] + a[j] + a[k]) / 3.0)
let calcUVBar (fvcom: FvcomGrid) (u, v) =
let h = fvcom.Bathymetry
@@ -356,24 +208,29 @@ let calcUVBar (fvcom: FvcomGrid) (u, v) =
[|
for z = 0 to nZ - 1 do
fvcom.Elem
|> Array.mapi (fun n (i, j, k) ->
hc[n] * (s[z, i] + s[z, j] + s[z, k] |> float)
/ 3.0)
|> Array.mapi (fun n (i, j, k) -> hc[n] * (s[z, i] + s[z, j] + s[z, k] |> float) / 3.0)
|]
|> matrix
let dz =
sigz.ToColumnArrays()
|> Array.map (
Array.pairwise
>> Array.map (fun (x0, x1) -> x1 - x0)
)
sigz.ToColumnArrays ()
|> Array.map (Array.pairwise >> Array.map (fun (x0, x1) -> x1 - x0))
|> matrix
|> fun x -> x.PointwiseAbs().Transpose()
|> fun x -> x.PointwiseAbs().Transpose ()
let bar (m: Matrix<float>) =
m.PointwiseMultiply dz
|> fun x ->
let s = x.ColumnSums()
let s = x.ColumnSums ()
s.PointwiseDivide hc
let u' = Matrix.Build.DenseOfRowArrays u
let v' = Matrix.Build.DenseOfRowArrays v
bar u', bar v'
// Linear interpolation to nearest vertical neighbor
let mkAllDepthInterpolCoefs (fvcom: Fvcom.FvcomGrid) (roms: AdjoinedGrid) =
Log.Information "Computing interpolation coefficients."
let uv = Array.zip roms.u roms.v |> Array.map (fun (u, v) -> (u + v) * 0.5)
{
iRho = mkDepthInterpolCoefs fvcom.Siglay roms.h roms.zRho
iU = mkDepthInterpolCoefs fvcom.SiglayCenter uv roms.zU
iV = mkDepthInterpolCoefs fvcom.SiglayCenter uv roms.zV
}

View File

@@ -1,68 +1,67 @@
module Oceanbox.FvcomKit.Smoothing
open Fvcom
open Oceanbox.FvcomKit.Fvcom
open Oceanbox.FvcomKit.Grid
open Serilog
open Grid
open ProjNet.FSharp
let private OOBVAL = -100000f
let smooth (prop: single []) source =
let smooth (prop: single[]) source =
source
|> Array.fold (fun a x -> a + prop[x]) 0f
|> fun x -> x / single source.Length
let smooth' (prop: single []) source =
let smooth' (prop: single[]) source =
source
|> Array.fold (fun (n, a) x -> if prop[x] > OOBVAL then n + 1, a + prop[x] else n, a) (0, 0f)
|> fun (n, a) -> a / single n
let smoothNodes (idx: NeighborIndex) (prop: single []) =
idx.NodesAroundNode.Values
|> Seq.toArray
|> Array.Parallel.map (smooth prop)
let smoothNodes (idx: NeighborIndex) (prop: single[]) =
idx.NodesAroundNode.Values |> Seq.toArray |> Array.Parallel.map (smooth prop)
let smoothNodes2D (idx: NeighborIndex) (prop: single [,]) =
let smoothNodes2D (idx: NeighborIndex) (prop: single[,]) =
for k = 0 to Array2D.length1 prop - 1 do
Log.Debug $"2D smoothing nodes z = {k}"
prop[k, *] <- idx.NodesAroundNode.Values
|> Seq.toArray
|> Array.Parallel.map (smooth prop[k, *])
prop[k, *] <-
idx.NodesAroundNode.Values
|> Seq.toArray
|> Array.Parallel.map (smooth prop[k, *])
prop
let smoothNodes3D (idx: NeighborIndex) (prop: single [,,]) =
let smoothNodes3D (idx: NeighborIndex) (prop: single[,,]) =
for k = 0 to Array3D.length1 prop - 1 do
for n = 0 to Array3D.length2 prop - 1 do
Log.Debug $"3D smoothing nodes z = {n}"
prop[k, n, *] <- idx.NodesAroundNode.Values
|> Seq.toArray
|> Array.Parallel.map (smooth prop[k, n, *])
prop[k, n, *] <-
idx.NodesAroundNode.Values
|> Seq.toArray
|> Array.Parallel.map (smooth prop[k, n, *])
prop
let smoothElements (idx: NeighborIndex) (grid: Grid) (prop: single []) =
let smoothElements (idx: NeighborIndex) (grid: Grid) (prop: single[]) =
grid.Elem
|> Array.Parallel.mapi (fun n _ ->
let elems = getElemsSurroundingElem idx grid n
smooth prop elems)
let smoothElements2D (idx: NeighborIndex) (grid: Grid) (prop: single [,]) =
let smoothElements2D (idx: NeighborIndex) (grid: Grid) (prop: single[,]) =
for k = 0 to Array2D.length1 prop - 1 do
Log.Debug $"2D smoothing elements z = {k}"
prop[k, *] <- smoothElements idx grid prop[k, *]
prop
let smoothElements3D (idx: NeighborIndex) (grid: Grid) (prop: single [,,]) =
let smoothElements3D (idx: NeighborIndex) (grid: Grid) (prop: single[,,]) =
for k = 0 to Array3D.length1 prop - 1 do
for n = 0 to Array3D.length2 prop - 1 do
Log.Debug $"3D smoothing elements z = {n}"
prop[k, n, *] <- smoothElements idx grid prop[k, n, *]
prop
let private rectifyOob (getSurrounding: int -> int []) (oob: int []) (prop: single []) =
let private rectifyOob (getSurrounding: int -> int[]) (oob: int[]) (prop: single[]) =
for i in oob do
prop[i] <- OOBVAL // reset
let rec rectify (oob: int []) =
let rec rectify (oob: int[]) =
Log.Debug $"rectify oob remaining {oob.Length}"
for n in oob do
let ns = getSurrounding n
@@ -74,60 +73,36 @@ let private rectifyOob (getSurrounding: int -> int []) (oob: int []) (prop: sing
rectify oob
prop
let rectifyOutOfBoundsNodes (idx: NeighborIndex) (oob: int []) (prop: single []) =
let rectifyOutOfBoundsNodes (idx: NeighborIndex) (oob: int[]) (prop: single[]) =
let f = getNodesSurroundingNode idx
rectifyOob f oob prop
let rectifyOutOfBoundsNodes2D (idx: NeighborIndex) (oob: int []) (prop: single [,]) =
let rectifyOutOfBoundsNodes2D (idx: NeighborIndex) (oob: int[]) (prop: single[,]) =
let f = getNodesSurroundingNode idx
for k = 0 to Array2D.length1 prop - 1 do
prop[k, *] <- rectifyOob f oob prop[k, *]
prop
let rectifyOutOfBoundsNodes3D (idx: NeighborIndex) (oob: int []) (prop: single [,,]) =
let rectifyOutOfBoundsNodes3D (idx: NeighborIndex) (oob: int[]) (prop: single[,,]) =
let f = getNodesSurroundingNode idx
for k = 0 to Array3D.length1 prop - 1 do
for n = 0 to Array3D.length2 prop - 1 do
prop[k, n, *] <- rectifyOob f oob prop[k, n, *]
prop
let rectifyOutOfBoundsElements (idx: NeighborIndex) (grid: Grid) (oob: int []) (prop: single []) =
let rectifyOutOfBoundsElements (idx: NeighborIndex) (grid: Grid) (oob: int[]) (prop: single[]) =
let f = getElemsSurroundingElem idx grid
rectifyOob f oob prop
let rectifyOutOfBoundsElements2D (idx: NeighborIndex) (grid: Grid) (oob: int []) (prop: single [,]) =
let rectifyOutOfBoundsElements2D (idx: NeighborIndex) (grid: Grid) (oob: int[]) (prop: single[,]) =
let f = getElemsSurroundingElem idx grid
for k = 0 to Array2D.length1 prop - 1 do
prop[k, *] <- rectifyOob f oob prop[k, *]
prop
let rectifyOutOfBoundsElements3D (idx: NeighborIndex) (grid: Grid) (oob: int []) (prop: single [,,]) =
let rectifyOutOfBoundsElements3D (idx: NeighborIndex) (grid: Grid) (oob: int[]) (prop: single[,,]) =
let f = getElemsSurroundingElem idx grid
for k = 0 to Array3D.length1 prop - 1 do
for n = 0 to Array3D.length2 prop - 1 do
prop[k, n, *] <- rectifyOob f oob prop[k, n, *]
prop
let rectifyOutOfBoundProps (fvcom: Grid) rGrid idx (x: FvcomRestart) =
let g = fvcom
let a = ROMS.genRomsToFvcomAdjoints fvcom rGrid
{ x with
salinity = rectifyOutOfBoundsNodes3D idx a.rhoAdjoint.oobIdx x.salinity
temp = rectifyOutOfBoundsNodes3D idx a.rhoAdjoint.oobIdx x.temp
zeta = rectifyOutOfBoundsNodes2D idx a.rhoAdjoint.oobIdx x.zeta
u = rectifyOutOfBoundsElements3D idx g a.uAdjoint.oobIdx x.u
v = rectifyOutOfBoundsElements3D idx g a.vAdjoint.oobIdx x.v
ua = rectifyOutOfBoundsElements2D idx g a.uAdjoint.oobIdx x.ua
va = rectifyOutOfBoundsElements2D idx g a.vAdjoint.oobIdx x.va
}
let smoothProps idx (grid: Grid) (x: FvcomRestart) =
{ x with
salinity = smoothNodes3D idx x.salinity
temp = smoothNodes3D idx x.temp
zeta = smoothNodes2D idx x.zeta
u = smoothElements3D idx grid x.u
v = smoothElements3D idx grid x.v
ua = smoothElements2D idx grid x.ua
va = smoothElements2D idx grid x.va
}
prop

View File

@@ -1,6 +1,7 @@
module Oceanbox.FvcomKit.Thredds
open System
open Microsoft.Research.Science.Data
open Microsoft.Research.Science.Data.NetCDF4
open Serilog
@@ -21,21 +22,20 @@ type Kind =
| Avg -> "avg"
| Qck -> "qck"
let dateRange (start: DateTime) days =
let dateRange (start: DateTime) days : DateTime list =
if days < 0 then
List.unfold (fun d -> if d < days then None else Some(start.AddDays d, d - 1)) 0
List.unfold (fun d -> if d < days then None else Some (start.AddDays d, d - 1)) 0
else
List.unfold (fun d -> if d > days then None else Some(start.AddDays d, d + 1)) 0
List.unfold (fun d -> if d > days then None else Some (start.AddDays d, d + 1)) 0
let tryOpenArchive url =
let uri = NetCDFUri()
uri.Url <- url
let tryOpenArchive url : DataSet option =
let uri = NetCDFUri ()
do uri.Url <- url
try
let ds = NetCDFDataSet.Open uri
Some ds
with
| e ->
Log.Debug e.Message
with e ->
do Log.Debug e.Message
None
let rec tryOpenThredds =
@@ -43,18 +43,22 @@ let rec tryOpenThredds =
| [] -> None
| x :: xs ->
match tryOpenArchive x with
| None ->
tryOpenThredds xs
| Some ds ->
Log.Debug $"thredds: {x}"
Some(x, ds)
| None -> tryOpenThredds xs
do Log.Debug $"thredds: {x}"
Some (x, ds)
let tryGetTimeIndex (ds: DataSet) (t: DateTime) =
let ot = ds[ "ocean_time" ].GetData() :?> double []
let t0 = DateTimeOffset.FromUnixTimeSeconds(ot[0] |> int64)
let t1 = DateTimeOffset.FromUnixTimeSeconds(ot[^0] |> int64)
Log.Debug $"t={t} t0={t0} tn={t1}"
let ot = ds["ocean_time"].GetData () :?> double[]
let first = Array.head ot
let last = Array.last ot
let t0 = DateTimeOffset.FromUnixTimeSeconds (first |> int64)
let t1 = DateTimeOffset.FromUnixTimeSeconds (last |> int64)
do Log.Debug $"t={t} t0={t0} tn={t1}"
if t < t0.DateTime || t > t1.DateTime then
Log.Error "time is out of bounds"
do Log.Error "time is out of bounds"
None
else
let dt = t - t0.DateTime

View File

@@ -2,7 +2,26 @@ module Oceanbox.FvcomKit.Types
open System
let unzip2D (array: _ [,]) =
type Vertex = float * float
[<Struct>]
type BBox = {
minX: float
maxX: float
minY: float
maxY: float
center: float * float
} with
static member empty = {
minX = Double.MaxValue
maxX = Double.MinValue
minY = Double.MaxValue
maxY = Double.MinValue
center = 0, 0
}
let unzip2D (array: _[,]) =
let len1 = Array2D.length1 array
let len2 = Array2D.length2 array
let res1 = Array2D.zeroCreate len1 len2
@@ -14,7 +33,7 @@ let unzip2D (array: _ [,]) =
res2[i, j] <- y
res1, res2
let inline propTo3D (p: 'a [,]) =
let inline propTo3D (p: 'a[,]) =
let z = Array2D.length1 p
let n = Array2D.length2 p
let m = Array3D.zeroCreate 1 z n
@@ -23,7 +42,7 @@ let inline propTo3D (p: 'a [,]) =
m[0, i, j] <- p[i, j] |> single
m
let inline propTo2D (p: 'a []) =
let inline propTo2D (p: 'a[]) =
let n = p.Length
let m = Array2D.zeroCreate 1 n
for i = 0 to n - 1 do

41
src/default.nix Normal file
View File

@@ -0,0 +1,41 @@
{
SDSLite,
projnet,
dotnet-sdk,
dotnet-runtime,
buildDotnetModule,
}:
let
name = "Oceanbox.Fvcomkit";
projectFile = ./Oceanbox.FvcomKit.fsproj;
versionMatch = builtins.match ".*<Version>([^<]+)</Version>.*" (
builtins.readFile projectFile
);
version = builtins.head versionMatch;
in
buildDotnetModule {
name = name;
pname = name;
version = version;
src = ./.;
buildInputs = [
projnet
SDSLite
];
projectFile = "Oceanbox.FvcomKit.fsproj";
inherit
dotnet-sdk
dotnet-runtime
;
nugetDeps = ./deps.json;
packNupkg = true;
# NOTE(mrtz): Can't package nuget without it
# [ref](https://github.com/dotnet/fsharp/issues/12320)
dotnetFlags = "--property:TargetsForTfmSpecificContentInPackage=";
}

272
src/deps.json Normal file
View File

@@ -0,0 +1,272 @@
[
{
"pname": "DynamicInterop",
"version": "0.9.1",
"hash": "sha256-IB76dA0+K/y/2s/qYL7AfVOF0+6W2hVIBgf9YdZ1oJY="
},
{
"pname": "FSharp.Core",
"version": "9.0.303",
"hash": "sha256-AxR6wqodeU23KOTgkUfIgbavgbcSuzD4UBP+tiFydgA="
},
{
"pname": "FSharp.Data",
"version": "6.3.0",
"hash": "sha256-zhVkSfqCljqr6UR0IUMOHUBlR61PvwYKq63PQ09yJPM="
},
{
"pname": "FSharp.Data",
"version": "6.4.1",
"hash": "sha256-+Z7zbD8cKmhHJWg7Z8XHJ8IeJXCWr/kgRl+VbbsMFw8="
},
{
"pname": "FSharp.Data.Csv.Core",
"version": "6.3.0",
"hash": "sha256-JdOr3NDmLPohkPpZaWjKqssw0+Wr1lVDtJwTNJ/JhcY="
},
{
"pname": "FSharp.Data.Csv.Core",
"version": "6.4.1",
"hash": "sha256-oz040beVF7WMONi3n0dPQlZD5deQWnClSXKRijgnw/k="
},
{
"pname": "FSharp.Data.Html.Core",
"version": "6.3.0",
"hash": "sha256-tSstVvAT9o+0Pr6cIReJOvh0kcthOWgt1CPzgIRoYRQ="
},
{
"pname": "FSharp.Data.Html.Core",
"version": "6.4.1",
"hash": "sha256-QBbvE8WXUVjS/0mW3aohZBuyfr3M7UGw7kt1oSrlq+s="
},
{
"pname": "FSharp.Data.Http",
"version": "6.3.0",
"hash": "sha256-/PzzLT0ev4miFswct+YscFDwoaq05BSJATM4fPvxk8o="
},
{
"pname": "FSharp.Data.Http",
"version": "6.4.1",
"hash": "sha256-0YD/jSCppE1siXrUcTx0OmVdgsjMk+gn0pHp+3GS3V4="
},
{
"pname": "FSharp.Data.Json.Core",
"version": "6.3.0",
"hash": "sha256-MFe88psxmHWGQYoG8NXi4z33TlWO+dMwOV4NViaUmTM="
},
{
"pname": "FSharp.Data.Json.Core",
"version": "6.4.1",
"hash": "sha256-0Fmo0f1jC3s+Dime8j2oqLnOK+elqo1xWmktpEYrZlk="
},
{
"pname": "FSharp.Data.Runtime.Utilities",
"version": "6.3.0",
"hash": "sha256-psc/tsHLYrorjeBBBLviwwA57XMFXUP2ywZqLMzfxac="
},
{
"pname": "FSharp.Data.Runtime.Utilities",
"version": "6.4.1",
"hash": "sha256-rNo2XQMME1zrPaIezD15P0RoTu8wyhtiJyB99Qp1hcE="
},
{
"pname": "FSharp.Data.WorldBank.Core",
"version": "6.3.0",
"hash": "sha256-QdL5ylUCvvrhvnnSPWj4MfN7B78hMbb5IRmozK7oJjM="
},
{
"pname": "FSharp.Data.WorldBank.Core",
"version": "6.4.1",
"hash": "sha256-nUyyziwpY58UnBNpqFoe/1bgDfQIq6gOqtQIwAo7x/c="
},
{
"pname": "FSharp.Data.Xml.Core",
"version": "6.3.0",
"hash": "sha256-67ftkfQJZ3iD62YKFh8Tu9Fuusb96KlKWxgykP1Wd9U="
},
{
"pname": "FSharp.Data.Xml.Core",
"version": "6.4.1",
"hash": "sha256-ZiD2aiD5yUZR4CDZl6mh4ji1G3xvm4Yd9Gya/e+D32w="
},
{
"pname": "FSharpPlus",
"version": "1.5.0",
"hash": "sha256-jQUlF3hsi3xpg+AdTnQw2L+lzbvTh5BIyLXCdVT6u6M="
},
{
"pname": "FSharpPlus",
"version": "1.7.0",
"hash": "sha256-6hDoDOnMFXQC5Hrk6Fhd+Wj+PbPFXzL9+xLIqgILJuY="
},
{
"pname": "FsPickler",
"version": "5.3.2",
"hash": "sha256-hjtm55aPJllzcVMPjFP4KYiEEBYtCcrUhbVOR+34agg="
},
{
"pname": "KdTree",
"version": "1.4.1",
"hash": "sha256-R4+L26pJoliLiwMuxmJDoa3Vf16gBq417fN+iNCy7Yc="
},
{
"pname": "MathNet.Numerics",
"version": "5.0.0",
"hash": "sha256-RHJCVM6OxquJF7n5Mbe/oNbucBbkge6ULcbAczOgmVo="
},
{
"pname": "MathNet.Numerics.FSharp",
"version": "5.0.0",
"hash": "sha256-pPbh8JdmMjBgEu84c/qV4YJ+LLr4+c31C6t++u29qBs="
},
{
"pname": "MessagePack",
"version": "3.1.3",
"hash": "sha256-OBn7iltr/rdE7ZKmv0MCUQSS+6OJKUYtlHdTbhEwzzE="
},
{
"pname": "MessagePack.Annotations",
"version": "3.1.3",
"hash": "sha256-o+T3u+xaHtW1c7AeWysCmIDUfN8lRhes2LoW5iQBafs="
},
{
"pname": "MessagePackAnalyzer",
"version": "3.1.3",
"hash": "sha256-5t4Av4CQ8HI7y9aAw+2qcOp+fsY0/3PdaFPJeCEAXQ0="
},
{
"pname": "Microsoft.NET.StringTools",
"version": "17.11.4",
"hash": "sha256-lWfzY35WQ+iKS9TpuztDTljgF9CIORhFhFEm0p1dVBE="
},
{
"pname": "Microsoft.NETCore.Platforms",
"version": "1.1.0",
"hash": "sha256-FeM40ktcObQJk4nMYShB61H/E8B7tIKfl9ObJ0IOcCM="
},
{
"pname": "Microsoft.NETCore.Targets",
"version": "1.1.0",
"hash": "sha256-0AqQ2gMS8iNlYkrD+BxtIg7cXMnr9xZHtKAuN4bjfaQ="
},
{
"pname": "ProjNET",
"version": "2.0.0",
"hash": "sha256-GjBnuGXmdFagIw9mX51Kpu/nn4gXta6a0cK/dxOWaZY="
},
{
"pname": "runtime.any.System.IO",
"version": "4.3.0",
"hash": "sha256-vej7ySRhyvM3pYh/ITMdC25ivSd0WLZAaIQbYj/6HVE="
},
{
"pname": "runtime.any.System.Reflection",
"version": "4.3.0",
"hash": "sha256-ns6f++lSA+bi1xXgmW1JkWFb2NaMD+w+YNTfMvyAiQk="
},
{
"pname": "runtime.any.System.Reflection.Primitives",
"version": "4.3.0",
"hash": "sha256-LkPXtiDQM3BcdYkAm5uSNOiz3uF4J45qpxn5aBiqNXQ="
},
{
"pname": "runtime.any.System.Runtime",
"version": "4.3.0",
"hash": "sha256-qwhNXBaJ1DtDkuRacgHwnZmOZ1u9q7N8j0cWOLYOELM="
},
{
"pname": "runtime.any.System.Text.Encoding",
"version": "4.3.0",
"hash": "sha256-Q18B9q26MkWZx68exUfQT30+0PGmpFlDgaF0TnaIGCs="
},
{
"pname": "runtime.any.System.Threading.Tasks",
"version": "4.3.0",
"hash": "sha256-agdOM0NXupfHbKAQzQT8XgbI9B8hVEh+a/2vqeHctg4="
},
{
"pname": "runtime.native.System",
"version": "4.3.0",
"hash": "sha256-ZBZaodnjvLXATWpXXakFgcy6P+gjhshFXmglrL5xD5Y="
},
{
"pname": "runtime.unix.System.Private.Uri",
"version": "4.3.0",
"hash": "sha256-c5tXWhE/fYbJVl9rXs0uHh3pTsg44YD1dJvyOA0WoMs="
},
{
"pname": "Serilog",
"version": "4.2.0",
"hash": "sha256-7f3EpCsEbDxXgsuhE430KVI14p7oDUuCtwRpOCqtnbs="
},
{
"pname": "Serilog.Sinks.Console",
"version": "6.0.0",
"hash": "sha256-QH8ykDkLssJ99Fgl+ZBFBr+RQRl0wRTkeccQuuGLyro="
},
{
"pname": "Serilog.Sinks.File",
"version": "6.0.0",
"hash": "sha256-KQmlUpG9ovRpNqKhKe6rz3XMLUjkBqjyQhEm2hV5Sow="
},
{
"pname": "Serilog.Sinks.Seq",
"version": "9.0.0",
"hash": "sha256-NnAkRbxwQGdNXz6DDONRxorNh1nqH2TfAQtokbq5qDw="
},
{
"pname": "System.IO",
"version": "4.3.0",
"hash": "sha256-ruynQHekFP5wPrDiVyhNiRIXeZ/I9NpjK5pU+HPDiRY="
},
{
"pname": "System.Memory",
"version": "4.5.3",
"hash": "sha256-Cvl7RbRbRu9qKzeRBWjavUkseT2jhZBUWV1SPipUWFk="
},
{
"pname": "System.Numerics.Vectors",
"version": "4.5.0",
"hash": "sha256-qdSTIFgf2htPS+YhLGjAGiLN8igCYJnCCo6r78+Q+c8="
},
{
"pname": "System.Private.Uri",
"version": "4.3.0",
"hash": "sha256-fVfgcoP4AVN1E5wHZbKBIOPYZ/xBeSIdsNF+bdukIRM="
},
{
"pname": "System.Reflection",
"version": "4.3.0",
"hash": "sha256-NQSZRpZLvtPWDlvmMIdGxcVuyUnw92ZURo0hXsEshXY="
},
{
"pname": "System.Reflection.Emit.ILGeneration",
"version": "4.3.0",
"hash": "sha256-mKRknEHNls4gkRwrEgi39B+vSaAz/Gt3IALtS98xNnA="
},
{
"pname": "System.Reflection.Emit.Lightweight",
"version": "4.3.0",
"hash": "sha256-rKx4a9yZKcajloSZHr4CKTVJ6Vjh95ni+zszPxWjh2I="
},
{
"pname": "System.Reflection.Primitives",
"version": "4.3.0",
"hash": "sha256-5ogwWB4vlQTl3jjk1xjniG2ozbFIjZTL9ug0usZQuBM="
},
{
"pname": "System.Runtime",
"version": "4.3.0",
"hash": "sha256-51813WXpBIsuA6fUtE5XaRQjcWdQ2/lmEokJt97u0Rg="
},
{
"pname": "System.Text.Encoding",
"version": "4.3.0",
"hash": "sha256-GctHVGLZAa/rqkBNhsBGnsiWdKyv6VDubYpGkuOkBLg="
},
{
"pname": "System.Threading.Tasks",
"version": "4.3.0",
"hash": "sha256-Z5rXfJ1EXp3G32IKZGiZ6koMjRu0n8C1NGrwpdIen4w="
}
]

View File

@@ -1,409 +0,0 @@
{
"version": 1,
"dependencies": {
"net6.0": {
"FSharp.Core": {
"type": "Direct",
"requested": "[6.0.4-beta.22181.2, )",
"resolved": "6.0.4-beta.22181.2",
"contentHash": "r2po3cDh7SvAQJAozPxlThH/aReaRMl0W5AqeMBE/pbLcJWoeavYpRWhs5VYdqJynxe9h2sgKvLIXa7zp7QhUw=="
},
"FSharp.Data": {
"type": "Direct",
"requested": "[4.2.8, )",
"resolved": "4.2.8",
"contentHash": "iSMHBxtovIvgMbMtJHetk2+pqDsVH4VBnz0VXkJ8wsnY9FYA2yaKU9Ntlg25Z383IWO/xBmLaBxw6QrpAT7g+g==",
"dependencies": {
"FSharp.Core": "4.7.2"
}
},
"FSharpPlus": {
"type": "Direct",
"requested": "[1.2.3.1-PR476-02311, )",
"resolved": "1.2.3.1-PR476-02311",
"contentHash": "dWAE0e24fgMMod3fPP6qS2Qn7AkhwweunhjSOsZ7dZAZIuxhSB+33x0TNLM6YUPOJmgkpsURs6aqY0RRDM+P7A==",
"dependencies": {
"FSharp.Core": "4.6.2"
}
},
"KdTree": {
"type": "Direct",
"requested": "[1.4.1, )",
"resolved": "1.4.1",
"contentHash": "yWbb35v/V9y88SLLMUPTlAN3pQEoPhDfZf9PApFnlU4kLtwVQ75U9vW5mW4/alQnLBuLKWBKcy4W5xK95mYsuA=="
},
"MathNet.Numerics.FSharp": {
"type": "Direct",
"requested": "[5.0.0, )",
"resolved": "5.0.0",
"contentHash": "lKYhd68fReW5odX/q+Uzxw3357Duq3zmvkYvnZVqqcc2r/EmrYGDoOdUGuHnhfr8yj9V34js5gQH/7IWcxZJxg==",
"dependencies": {
"FSharp.Core": "6.0.2",
"MathNet.Numerics": "5.0.0"
}
},
"ProjNet.FSharp": {
"type": "Direct",
"requested": "[3.0.1, )",
"resolved": "3.0.1",
"contentHash": "KU5y56kTrmHZVHyZQujQyhOf9PXscT7JR1dvJ0S1A8d23w79iB0nam1g8mtxHJWWDLIb1K2Bs54G+zynD4DLIQ==",
"dependencies": {
"FSharp.Core": "6.0.2",
"FSharp.Data": "4.2.7",
"FSharpPlus": "1.2.2",
"ProjNet": "2.0.0"
}
},
"SDSLite": {
"type": "Direct",
"requested": "[2.3.0, )",
"resolved": "2.3.0",
"contentHash": "AUa0jn38SPYeM17qIRTV0pbQsqRz6JDqwfnY83PtDrbYzn9PGJ7252dRHjY9ALoBlmTYsPYQyEmFHxY3KmVGsw==",
"dependencies": {
"DynamicInterop": "0.9.1"
}
},
"Serilog": {
"type": "Direct",
"requested": "[2.11.0, )",
"resolved": "2.11.0",
"contentHash": "ysv+hBzTul6Dp+Hvm10FlhJO3yMQcFKSAleus+LpiIzvNstpeV4Z7gGuIZ1OPNfIMulSHOjmLuGAEDKzpnV8ZQ=="
},
"Serilog.Sinks.Console": {
"type": "Direct",
"requested": "[4.0.2-dev-00890, )",
"resolved": "4.0.2-dev-00890",
"contentHash": "5l0OWV0dk0fAzascJUY0tWrAPqrleSs2uhZzW2y9iYXFYtEfMBPTi1+UHPO6Pa76Z4ZG06s2mpGfjAdPSpIxNw==",
"dependencies": {
"Serilog": "2.10.0"
}
},
"Serilog.Sinks.Seq": {
"type": "Direct",
"requested": "[5.1.2-dev-00222, )",
"resolved": "5.1.2-dev-00222",
"contentHash": "Ef/wXQM2/jj7YK1xsiew5SUkHoORVLxvTyYWrqls+3SgKrWWvrOksVuRADhAZsGihZZ1hOMDrN00y4ycQl10NQ==",
"dependencies": {
"Serilog": "2.10.0",
"Serilog.Formatting.Compact": "1.1.0",
"Serilog.Sinks.File": "4.0.0",
"Serilog.Sinks.PeriodicBatching": "2.3.0"
}
},
"Thoth.Json.Net": {
"type": "Direct",
"requested": "[8.0.0, )",
"resolved": "8.0.0",
"contentHash": "C/b+8g/xUTJTn7pbKC4bMAOy2tyolXAuHTXguT5TNzDKQ6sjnUfFa9B81fTt9PuUOdWFLyRKlXASuFhSQciJGQ==",
"dependencies": {
"FSharp.Core": "4.7.2",
"Fable.Core": "[3.0.0, 4.0.0)",
"Newtonsoft.Json": "11.0.2"
}
},
"DynamicInterop": {
"type": "Transitive",
"resolved": "0.9.1",
"contentHash": "n21+Hd+tceX8lgaOosPV+Pne+YqnZUd5RLW3OhnsVxWRzYXiAIAKmKweHIePYeY+fmcn3N5tjkJyQUccFuL3bg=="
},
"Fable.Core": {
"type": "Transitive",
"resolved": "3.0.0",
"contentHash": "pkCOWJKAkCk36f5+q4F3XqlfsgCJL6i2lTLl4ZZVDswn8rjXo21EBG/gJ296a88LVBkI5LL2VwxQYqGZncomJw==",
"dependencies": {
"FSharp.Core": "4.5.2"
}
},
"MathNet.Numerics": {
"type": "Transitive",
"resolved": "5.0.0",
"contentHash": "pg1W2VwaEQMAiTpGK840hZgzavnqjlCMTVSbtVCXVyT+7AX4mc1o89SPv4TBlAjhgCOo9c1Y+jZ5m3ti2YgGgA=="
},
"Microsoft.NETCore.Platforms": {
"type": "Transitive",
"resolved": "1.0.1",
"contentHash": "2G6OjjJzwBfNOO8myRV/nFrbTw5iA+DEm0N+qUqhrOmaVtn4pC77h38I1jsXGw5VH55+dPfQsqHD0We9sCl9FQ=="
},
"Microsoft.NETCore.Targets": {
"type": "Transitive",
"resolved": "1.0.1",
"contentHash": "rkn+fKobF/cbWfnnfBOQHKVKIOpxMZBvlSHkqDWgBpwGDcLRduvs3D9OLGeV6GWGvVwNlVi2CBbTjuPmtHvyNw=="
},
"Newtonsoft.Json": {
"type": "Transitive",
"resolved": "11.0.2",
"contentHash": "IvJe1pj7JHEsP8B8J8DwlMEx8UInrs/x+9oVY+oCD13jpLu4JbJU2WCIsMRn5C4yW9+DgkaO8uiVE5VHKjpmdQ=="
},
"ProjNET": {
"type": "Transitive",
"resolved": "2.0.0",
"contentHash": "iMJG8qpGJ8SjFrB044O8wgo0raAWCdG1Bvly0mmVcjzsrexDHhC+dUct6Wb1YwQtupMBjSTWq7Fn00YeNErprA==",
"dependencies": {
"System.Memory": "4.5.3",
"System.Numerics.Vectors": "4.5.0"
}
},
"Serilog.Formatting.Compact": {
"type": "Transitive",
"resolved": "1.1.0",
"contentHash": "pNroKVjo+rDqlxNG5PXkRLpfSCuDOBY0ri6jp9PLe505ljqwhwZz8ospy2vWhQlFu5GkIesh3FcDs4n7sWZODA==",
"dependencies": {
"Serilog": "2.8.0"
}
},
"Serilog.Sinks.File": {
"type": "Transitive",
"resolved": "4.0.0",
"contentHash": "vBj43RkAbeP1dzoPFR2+LfV5GevDRPDq6265JJBv223lMvT9rfdwe/S/I9ow7aZSLYKfw4qPDw6NW8YwjbDbvg==",
"dependencies": {
"Serilog": "2.5.0",
"System.IO": "4.1.0",
"System.IO.FileSystem": "4.0.1",
"System.IO.FileSystem.Primitives": "4.0.1",
"System.Runtime.InteropServices": "4.1.0",
"System.Text.Encoding.Extensions": "4.0.11",
"System.Threading": "4.0.11",
"System.Threading.Timer": "4.0.1"
}
},
"Serilog.Sinks.PeriodicBatching": {
"type": "Transitive",
"resolved": "2.3.0",
"contentHash": "UYKSjTMTlUY9T3OgzMmLDLD+z0qPfgvq/RvG0rKfyz+O+Zrjw3X/Xpv14J4WMcGVsOjUaR+k8n2MdmqVpJtI6A==",
"dependencies": {
"Serilog": "2.0.0",
"System.Collections.Concurrent": "4.0.12",
"System.Threading.Timer": "4.0.1"
}
},
"System.Collections": {
"type": "Transitive",
"resolved": "4.0.11",
"contentHash": "YUJGz6eFKqS0V//mLt25vFGrrCvOnsXjlvFQs+KimpwNxug9x0Pzy4PlFMU3Q2IzqAa9G2L4LsK3+9vCBK7oTg==",
"dependencies": {
"Microsoft.NETCore.Platforms": "1.0.1",
"Microsoft.NETCore.Targets": "1.0.1",
"System.Runtime": "4.1.0"
}
},
"System.Collections.Concurrent": {
"type": "Transitive",
"resolved": "4.0.12",
"contentHash": "2gBcbb3drMLgxlI0fBfxMA31ec6AEyYCHygGse4vxceJan8mRIWeKJ24BFzN7+bi/NFTgdIgufzb94LWO5EERQ==",
"dependencies": {
"System.Collections": "4.0.11",
"System.Diagnostics.Debug": "4.0.11",
"System.Diagnostics.Tracing": "4.1.0",
"System.Globalization": "4.0.11",
"System.Reflection": "4.1.0",
"System.Resources.ResourceManager": "4.0.1",
"System.Runtime": "4.1.0",
"System.Runtime.Extensions": "4.1.0",
"System.Threading": "4.0.11",
"System.Threading.Tasks": "4.0.11"
}
},
"System.Diagnostics.Debug": {
"type": "Transitive",
"resolved": "4.0.11",
"contentHash": "w5U95fVKHY4G8ASs/K5iK3J5LY+/dLFd4vKejsnI/ZhBsWS9hQakfx3Zr7lRWKg4tAw9r4iktyvsTagWkqYCiw==",
"dependencies": {
"Microsoft.NETCore.Platforms": "1.0.1",
"Microsoft.NETCore.Targets": "1.0.1",
"System.Runtime": "4.1.0"
}
},
"System.Diagnostics.Tracing": {
"type": "Transitive",
"resolved": "4.1.0",
"contentHash": "vDN1PoMZCkkdNjvZLql592oYJZgS7URcJzJ7bxeBgGtx5UtR5leNm49VmfHGqIffX4FKacHbI3H6UyNSHQknBg==",
"dependencies": {
"Microsoft.NETCore.Platforms": "1.0.1",
"Microsoft.NETCore.Targets": "1.0.1",
"System.Runtime": "4.1.0"
}
},
"System.Globalization": {
"type": "Transitive",
"resolved": "4.0.11",
"contentHash": "B95h0YLEL2oSnwF/XjqSWKnwKOy/01VWkNlsCeMTFJLLabflpGV26nK164eRs5GiaRSBGpOxQ3pKoSnnyZN5pg==",
"dependencies": {
"Microsoft.NETCore.Platforms": "1.0.1",
"Microsoft.NETCore.Targets": "1.0.1",
"System.Runtime": "4.1.0"
}
},
"System.IO": {
"type": "Transitive",
"resolved": "4.1.0",
"contentHash": "3KlTJceQc3gnGIaHZ7UBZO26SHL1SHE4ddrmiwumFnId+CEHP+O8r386tZKaE6zlk5/mF8vifMBzHj9SaXN+mQ==",
"dependencies": {
"Microsoft.NETCore.Platforms": "1.0.1",
"Microsoft.NETCore.Targets": "1.0.1",
"System.Runtime": "4.1.0",
"System.Text.Encoding": "4.0.11",
"System.Threading.Tasks": "4.0.11"
}
},
"System.IO.FileSystem": {
"type": "Transitive",
"resolved": "4.0.1",
"contentHash": "IBErlVq5jOggAD69bg1t0pJcHaDbJbWNUZTPI96fkYWzwYbN6D9wRHMULLDd9dHsl7C2YsxXL31LMfPI1SWt8w==",
"dependencies": {
"Microsoft.NETCore.Platforms": "1.0.1",
"Microsoft.NETCore.Targets": "1.0.1",
"System.IO": "4.1.0",
"System.IO.FileSystem.Primitives": "4.0.1",
"System.Runtime": "4.1.0",
"System.Runtime.Handles": "4.0.1",
"System.Text.Encoding": "4.0.11",
"System.Threading.Tasks": "4.0.11"
}
},
"System.IO.FileSystem.Primitives": {
"type": "Transitive",
"resolved": "4.0.1",
"contentHash": "kWkKD203JJKxJeE74p8aF8y4Qc9r9WQx4C0cHzHPrY3fv/L/IhWnyCHaFJ3H1QPOH6A93whlQ2vG5nHlBDvzWQ==",
"dependencies": {
"System.Runtime": "4.1.0"
}
},
"System.Memory": {
"type": "Transitive",
"resolved": "4.5.3",
"contentHash": "3oDzvc/zzetpTKWMShs1AADwZjQ/36HnsufHRPcOjyRAAMLDlu2iD33MBI2opxnezcVUtXyqDXXjoFMOU9c7SA=="
},
"System.Numerics.Vectors": {
"type": "Transitive",
"resolved": "4.5.0",
"contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ=="
},
"System.Reflection": {
"type": "Transitive",
"resolved": "4.1.0",
"contentHash": "JCKANJ0TI7kzoQzuwB/OoJANy1Lg338B6+JVacPl4TpUwi3cReg3nMLplMq2uqYfHFQpKIlHAUVAJlImZz/4ng==",
"dependencies": {
"Microsoft.NETCore.Platforms": "1.0.1",
"Microsoft.NETCore.Targets": "1.0.1",
"System.IO": "4.1.0",
"System.Reflection.Primitives": "4.0.1",
"System.Runtime": "4.1.0"
}
},
"System.Reflection.Primitives": {
"type": "Transitive",
"resolved": "4.0.1",
"contentHash": "4inTox4wTBaDhB7V3mPvp9XlCbeGYWVEM9/fXALd52vNEAVisc1BoVWQPuUuD0Ga//dNbA/WeMy9u9mzLxGTHQ==",
"dependencies": {
"Microsoft.NETCore.Platforms": "1.0.1",
"Microsoft.NETCore.Targets": "1.0.1",
"System.Runtime": "4.1.0"
}
},
"System.Resources.ResourceManager": {
"type": "Transitive",
"resolved": "4.0.1",
"contentHash": "TxwVeUNoTgUOdQ09gfTjvW411MF+w9MBYL7AtNVc+HtBCFlutPLhUCdZjNkjbhj3bNQWMdHboF0KIWEOjJssbA==",
"dependencies": {
"Microsoft.NETCore.Platforms": "1.0.1",
"Microsoft.NETCore.Targets": "1.0.1",
"System.Globalization": "4.0.11",
"System.Reflection": "4.1.0",
"System.Runtime": "4.1.0"
}
},
"System.Runtime": {
"type": "Transitive",
"resolved": "4.1.0",
"contentHash": "v6c/4Yaa9uWsq+JMhnOFewrYkgdNHNG2eMKuNqRn8P733rNXeRCGvV5FkkjBXn2dbVkPXOsO0xjsEeM1q2zC0g==",
"dependencies": {
"Microsoft.NETCore.Platforms": "1.0.1",
"Microsoft.NETCore.Targets": "1.0.1"
}
},
"System.Runtime.Extensions": {
"type": "Transitive",
"resolved": "4.1.0",
"contentHash": "CUOHjTT/vgP0qGW22U4/hDlOqXmcPq5YicBaXdUR2UiUoLwBT+olO6we4DVbq57jeX5uXH2uerVZhf0qGj+sVQ==",
"dependencies": {
"Microsoft.NETCore.Platforms": "1.0.1",
"Microsoft.NETCore.Targets": "1.0.1",
"System.Runtime": "4.1.0"
}
},
"System.Runtime.Handles": {
"type": "Transitive",
"resolved": "4.0.1",
"contentHash": "nCJvEKguXEvk2ymk1gqj625vVnlK3/xdGzx0vOKicQkoquaTBJTP13AIYkocSUwHCLNBwUbXTqTWGDxBTWpt7g==",
"dependencies": {
"Microsoft.NETCore.Platforms": "1.0.1",
"Microsoft.NETCore.Targets": "1.0.1",
"System.Runtime": "4.1.0"
}
},
"System.Runtime.InteropServices": {
"type": "Transitive",
"resolved": "4.1.0",
"contentHash": "16eu3kjHS633yYdkjwShDHZLRNMKVi/s0bY8ODiqJ2RfMhDMAwxZaUaWVnZ2P71kr/or+X9o/xFWtNqz8ivieQ==",
"dependencies": {
"Microsoft.NETCore.Platforms": "1.0.1",
"Microsoft.NETCore.Targets": "1.0.1",
"System.Reflection": "4.1.0",
"System.Reflection.Primitives": "4.0.1",
"System.Runtime": "4.1.0",
"System.Runtime.Handles": "4.0.1"
}
},
"System.Text.Encoding": {
"type": "Transitive",
"resolved": "4.0.11",
"contentHash": "U3gGeMlDZXxCEiY4DwVLSacg+DFWCvoiX+JThA/rvw37Sqrku7sEFeVBBBMBnfB6FeZHsyDx85HlKL19x0HtZA==",
"dependencies": {
"Microsoft.NETCore.Platforms": "1.0.1",
"Microsoft.NETCore.Targets": "1.0.1",
"System.Runtime": "4.1.0"
}
},
"System.Text.Encoding.Extensions": {
"type": "Transitive",
"resolved": "4.0.11",
"contentHash": "jtbiTDtvfLYgXn8PTfWI+SiBs51rrmO4AAckx4KR6vFK9Wzf6tI8kcRdsYQNwriUeQ1+CtQbM1W4cMbLXnj/OQ==",
"dependencies": {
"Microsoft.NETCore.Platforms": "1.0.1",
"Microsoft.NETCore.Targets": "1.0.1",
"System.Runtime": "4.1.0",
"System.Text.Encoding": "4.0.11"
}
},
"System.Threading": {
"type": "Transitive",
"resolved": "4.0.11",
"contentHash": "N+3xqIcg3VDKyjwwCGaZ9HawG9aC6cSDI+s7ROma310GQo8vilFZa86hqKppwTHleR/G0sfOzhvgnUxWCR/DrQ==",
"dependencies": {
"System.Runtime": "4.1.0",
"System.Threading.Tasks": "4.0.11"
}
},
"System.Threading.Tasks": {
"type": "Transitive",
"resolved": "4.0.11",
"contentHash": "k1S4Gc6IGwtHGT8188RSeGaX86Qw/wnrgNLshJvsdNUOPP9etMmo8S07c+UlOAx4K/xLuN9ivA1bD0LVurtIxQ==",
"dependencies": {
"Microsoft.NETCore.Platforms": "1.0.1",
"Microsoft.NETCore.Targets": "1.0.1",
"System.Runtime": "4.1.0"
}
},
"System.Threading.Timer": {
"type": "Transitive",
"resolved": "4.0.1",
"contentHash": "saGfUV8uqVW6LeURiqxcGhZ24PzuRNaUBtbhVeuUAvky1naH395A/1nY0P2bWvrw/BreRtIB/EzTDkGBpqCwEw==",
"dependencies": {
"Microsoft.NETCore.Platforms": "1.0.1",
"Microsoft.NETCore.Targets": "1.0.1",
"System.Runtime": "4.1.0"
}
}
}
}
}

View File

@@ -1,18 +0,0 @@
module Tests
open Expecto
let server =
testList
"Server"
[
testCase "Adding valid Todo"
<| fun _ ->
let expectedResult = Ok()
Expect.equal (Ok()) expectedResult "Result should be ok"
]
let all = testList "All" [ server ]
[<EntryPoint>]
let main _ = runTests defaultConfig all

View File

@@ -1,17 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Compile Include="Tests.fs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\src\Oceanbox.FvcomKit.fsproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Expecto" Version="9.0.4" />
<PackageReference Update="FSharp.Core" Version="6.0.4-beta.22181.2" />
</ItemGroup>
</Project>

189
xtest/Arome.fs Normal file
View File

@@ -0,0 +1,189 @@
module Arome
open System
open Microsoft.Research.Science.Data
open Xunit
open FsUnit.Xunit
open FsUnit.CustomMatchers
open Serilog
open ProjNet.FSharp
open Oceanbox.FvcomKit
let logger =
LoggerConfiguration()
.MinimumLevel.Is(Events.LogEventLevel.Verbose)
.WriteTo.Console()
.CreateLogger()
do Log.Logger <- logger
[<Literal>]
let private path = "/data/archives/Arome/meps_det_sfc_20220102T00Z.nc"
let trans = makeTransform CoordSys.WGS84 (CoordSys.LCCMet ())
// Use Netcdf to open dataset
let private openDataset (path: string) : DataSet =
let sw = Diagnostics.Stopwatch.StartNew()
let uri = NetCDF4.NetCDFUri()
do uri.FileName <- path
do uri.OpenMode <- ResourceOpenMode.ReadOnly
do uri.Deflate <- NetCDF4.DeflateLevel.Off
let ds = NetCDF4.NetCDFDataSet.Open uri
do Log.Debug $"openDataSet: {path} completed in {sw.ElapsedMilliseconds}ms"
ds
let private aromeGrid = {
Arome.SquareGrid.empty with
dimensions = 949, 1069
BBox = {
minX = -18.12241427
maxX = 54.24126163
minY = 49.7653854
maxY = 75.22869642
center = 27.12063081, 37.61434821
}
squareSize = 2.444065489
}
[<Fact>]
let ``Create grid``() =
use ds = openDataset path
let res = Arome.getGrid ds
// "Should be able to create square grid from /data/archives/Arome/meps_det_sfc_20220102T00Z.nc"
res |> should be (ofCase <@ Result<Arome.SquareGrid, string>.Ok @>)
[<Fact>]
let ``test grid regularity``() =
let trans = makeTransform CoordSys.WGS84 (CoordSys.LCCMet ())
use ds = openDataset path
let dimensions = ds.Dimensions["x"].Length, ds.Dimensions["y"].Length
let longs : float array2d = ds["longitude"].GetData () :?> float[,]
let lats : float array2d = ds["latitude"].GetData () :?> float[,]
// NOTE: The netcdf file dimensions are defined as (y, x)
let width = Array2D.length2 longs
let height = Array2D.length1 lats
let points : Arome.Points array =
let result = Array.create (width * height) Arome.Points.Zero
for i in 0 .. height - 1 do
for j in 0 .. width - 1 do
let lat = lats[i, j]
let lon = longs[i, j]
let p = lon, lat
// NOTE(simkir): Convert to lambert and undo all I've done... :(
let x, y = trans.project p
result[i * width + j] <- Arome.Points.OfTuple (single x, single y)
result
let mutable acc = 0.0f
for i in 0 .. height - 1 do
for j in 0 .. width - 2 do
let p0 = points[i * width + j]
let p1 = points[i * width + j + 1]
let dx = if p0.x < p1.x then p1.x - p0.x else p0.x - p1.x
acc <- acc + dx
let count = width * height
let avgDiffX = acc / single count
Log.Debug("Avg. X distance: {Avg}m", avgDiffX)
acc <- 0.0f
for i in 0 .. height - 2 do
for j in 0 .. width - 1 do
let p0 = points[i * width + j]
let p1 = points[(i + 1) * width + j]
let dy = if p0.y < p1.y then p1.y - p0.y else p0.y - p1.y
acc <- acc + dy
let avgDiffY = acc / single count
Log.Debug("Avg. Y distance: {Avg}m", avgDiffY)
[<Fact>]
let ``point within first rect``() =
use ds = openDataset path
let res = Arome.getGrid ds
match res with
| Ok grid ->
let width, _height = grid.dimensions
let p0 = grid.points[0 * width + 0]
let p1 = grid.points[1 * width + 1]
let rect = Arome.Rect.OfPoints p0 p1
// { x = -1064984.25f y = -1353489.375f }, { x = -1062474.625f y = -1350985.875f }
let p2 = Arome.Points.OfTuple (-1063000.25f, -1353486.0f)
let within = Arome.Rect.pointWithin rect p2
within |> should equal true
| Error _ ->
failwith "Should create grid"
[<Fact>]
let ``Read uvs for index (1, 1)``() =
use ds = openDataset path
let res = Arome.getGrid ds
match res with
| Ok grid ->
let idx =
let lat = 50.35
let lon = 0.304
let p = trans.project((lon, lat))
Arome.tryFindIndex grid p
match idx with
| Some (i, j) ->
let u, v = Arome.readUV ds 0 i j
(u, v) |> should equal (8.530239f, 6.659096f) // "Find uv (8.530239, 6.659096) at index (1, 1)"
| None ->
failwith "Should find idx"
| Error _ ->
failwith "Should find grid"
[<Fact>]
let ``Try search index (1, 1069) based on map coords``() =
let lat = 72.800
let lon = -17.850
let p = trans.project((lon, lat))
let res =
use ds = openDataset path
Arome.getGrid ds
match res with
| Ok grid ->
let idx = Arome.tryFindIndex grid p
idx |> should equal (Some (4, 1067)) // "Should find idx (1, 1069)"
| Error _ ->
failwith "Should find grid"
[<Fact>]
let ``Read uvs for pos (60.3562, 5.2178)``() =
let lat = 60.3562
let lon = 5.2178
let p = trans.project((lon, lat))
use ds = openDataset path
let res = Arome.getGrid ds
match res with
| Ok grid ->
let idx = Arome.tryFindIndex grid p
match idx with
| Some (i, j) ->
let u, v = Arome.readUV ds 0 i j
// "Find uv (2.367153168, -0.3955917358) at pos (60.3562, 5.2178) almost in Bergen"
(u, v) |> should equal (-0.3750343323f, 7.744056702f)
| None ->
failwith "Should find idx"
| Error _ ->
failwith "Should find grid"
[<Fact>]
let ``Read uvs for t 23 x 43 y 144``() =
use ds = openDataset path
let u, v = Arome.readUV ds 23 43 144
// "Find uv (2.367153168, -0.3955917358) at pos (60.3562, 5.2178) almost in Bergen"
(u, v) |> should equal (11.77926254f, 5.764093399f)

38
xtest/xtest.fsproj Normal file
View File

@@ -0,0 +1,38 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Nullable>enable</Nullable>
<OutputType>Exe</OutputType>
<RootNamespace>xtest</RootNamespace>
<TargetFramework>net9.0</TargetFramework>
<RuntimeIdentifier>linux-x64</RuntimeIdentifier>
<!--
This template uses native xUnit.net command line options when using 'dotnet run' and
VSTest by default when using 'dotnet test'. For more information on how to enable support
for Microsoft Testing Platform, please visit:
https://xunit.net/docs/getting-started/v3/microsoft-testing-platform
-->
</PropertyGroup>
<ItemGroup>
<Content Include="xunit.runner.json" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
<ItemGroup>
<Compile Include="Arome.fs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="FsUnit" Version="7.1.1" />
<PackageReference Include="FsUnit.xUnit" Version="7.1.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageReference Include="Oceanbox.SDSLite" Version="2.8.0" />
<PackageReference Include="xunit.v3" Version="3.2.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\src\Oceanbox.FvcomKit.fsproj" />
</ItemGroup>
</Project>

3
xtest/xunit.runner.json Normal file
View File

@@ -0,0 +1,3 @@
{
"$schema": "https://xunit.net/schema/current/xunit.runner.schema.json"
}