Compare commits
137 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a38dba5728 | ||
| 083e5dac5b | |||
| 622d9837fd | |||
| 655abebe52 | |||
| 18a9d70698 | |||
|
|
aaa597c52e | ||
|
|
571302a86b | ||
|
71e861e417
|
|||
|
|
bec03ed5ec | ||
| 75ba2abcfe | |||
|
0c55b2dcbd
|
|||
|
|
c9b3320464 | ||
|
|
bfb3a8e489 | ||
|
8715f4d8c3
|
|||
|
c89f35bc6e
|
|||
|
|
c22ae77301 | ||
| c4513c0b09 | |||
| 06a4aeabf1 | |||
|
|
65fbf66016 | ||
| bfbaa3aeff | |||
| ad148f6284 | |||
| 33b7b999c8 | |||
|
|
80f538fcb0 | ||
| 6b16e07adc | |||
| 2e87294c46 | |||
|
|
76cd56e1ab | ||
| 47279b49cb | |||
| abdb949113 | |||
|
|
5464bc34f8 | ||
| 3086ef5ebf | |||
| 21e84d07cd | |||
|
|
376891e4a0 | ||
| f19a74fc9d | |||
| cef71eb90a | |||
| 222b022662 | |||
| bf7ae5889b | |||
|
|
817c02b5a6 | ||
|
|
86e1307d79 | ||
|
|
9abd9d45bc | ||
|
|
02de20d50b | ||
|
|
c8a838c1e6 | ||
|
|
17685ac2cd | ||
|
|
6662ad4ebf | ||
|
|
46209553a8 | ||
|
|
ed0ac797d3 | ||
|
|
63dc1f2cca | ||
|
|
0f5de91d39 | ||
|
|
f2730ad1e3 | ||
|
|
ef172fa883 | ||
|
|
8d7ab4164c | ||
|
|
ff283cd910 | ||
|
|
35e1f43fc0 | ||
|
|
3f61f1d1a9 | ||
|
|
ac5aad659b | ||
|
|
746f58b29e | ||
|
|
bc130658e9 | ||
|
|
aba0917557 | ||
|
|
72a49a8039 | ||
|
|
d75db35d8b | ||
|
|
94f5eb56a0 | ||
|
|
cfbf2b1d61 | ||
|
|
e09caf7a12 | ||
|
|
0f2b69609c | ||
|
|
8152537ee7 | ||
|
|
b1341581a1 | ||
|
|
d2e362df20 | ||
|
|
f38f06c936 | ||
|
|
90b50c3c83 | ||
|
|
5f5ad1b9e4 | ||
|
|
4ca7fff09e | ||
|
|
7e9e8db9cb | ||
|
|
5479c8b598 | ||
|
|
de0a4a2751 | ||
|
|
902ac080e9 | ||
|
|
22b65f878a | ||
|
|
c53c2b5b05 | ||
|
|
86d250916e | ||
|
|
4556a9fd9a | ||
|
|
b67653179f | ||
|
|
5a1ffcdf36 | ||
|
|
540a913fb4 | ||
|
|
4b889998ac | ||
|
|
01183f7469 | ||
|
|
d749f1b157 | ||
|
|
1bba6431c4 | ||
|
|
29a7c6c148 | ||
|
|
672e52cb8b | ||
|
|
f6ee353980 | ||
|
|
0d0bf9e873 | ||
|
|
eb2a981576 | ||
|
|
ff947bb364 | ||
|
|
36bd52d3f1 | ||
|
|
48ea6dd573 | ||
|
|
5511abbbfc | ||
|
|
460f8f10eb | ||
|
|
764bdedb45 | ||
|
|
82b4ceea36 | ||
|
|
434ab6d5f4 | ||
|
|
302c4f1048 | ||
|
|
e7bb5540ef | ||
|
|
bb57404523 | ||
|
|
86384bfd9f | ||
|
|
2be8397297 | ||
|
|
e9f10c8f12 | ||
|
|
6881554909 | ||
|
|
a6a8828ae9 | ||
|
|
a9d7662a2b | ||
|
|
774c840a84 | ||
|
|
41841e0d2a | ||
|
|
7b3a14e073 | ||
|
|
b1a6225f5e | ||
|
|
bb73564a00 | ||
|
|
347a730e13 | ||
|
|
7d81ac771a | ||
|
|
efeec1519e | ||
|
|
886234b165 | ||
|
|
bab002d1a6 | ||
|
|
f851e5411b | ||
|
|
af4833b555 | ||
|
|
0bf255d510 | ||
|
|
ce8b1e593b | ||
|
|
13e689d68e | ||
|
|
3305417c4b | ||
|
|
ab63c39993 | ||
|
|
1caa90c246 | ||
|
|
8c1c3e750f | ||
|
|
23fea2fd82 | ||
|
|
a2b6a3b04f | ||
|
|
116a714748 | ||
|
|
0bf18891b2 | ||
|
|
2ae5666f01 | ||
|
|
1939213ec4 | ||
|
|
7164c1793a | ||
|
|
7c977abaa3 | ||
|
|
2b361738a6 | ||
|
|
e7da5922cf | ||
|
|
8cd202a699 |
@@ -18,7 +18,7 @@ let versionFile = Path.getFullName ".version"
|
||||
Target.create "Clean" (fun _ -> Shell.cleanDir distPath)
|
||||
|
||||
Target.create "InstallClient" (fun _ ->
|
||||
run npm "install" "."
|
||||
run bun "install" "."
|
||||
run dotnet "tool restore" "."
|
||||
)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"version": 1,
|
||||
"isRoot": true,
|
||||
"tools": {
|
||||
"fable": {
|
||||
"version": "3.7.0",
|
||||
"commands": [
|
||||
"fable"
|
||||
]
|
||||
},
|
||||
"fantomas-tool": {
|
||||
"version": "4.6.4",
|
||||
"commands": [
|
||||
"fantomas"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
.gitignore
vendored
3
.gitignore
vendored
@@ -15,3 +15,6 @@ deploy
|
||||
.ionide/
|
||||
*.db
|
||||
build.fsx.lock
|
||||
dist/
|
||||
.direnv/
|
||||
result*
|
||||
@@ -1,10 +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: .
|
||||
|
||||
8
.gitmodules
vendored
8
.gitmodules
vendored
@@ -1,8 +0,0 @@
|
||||
[submodule "submodules/ProjNet.FSharp"]
|
||||
path = submodules/ProjNet.FSharp
|
||||
url = ../ProjNet.FSharp
|
||||
shallow = true
|
||||
[submodule "submodules/FsKdTree"]
|
||||
path = submodules/FsKdTree
|
||||
url = https://gitlab.com/serit/libs/FsKDTree.git
|
||||
shallow = true
|
||||
14
Build.fsproj
14
Build.fsproj
@@ -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.23.0" />
|
||||
<PackageReference Include="Fake.DotNet.Cli" Version="5.23.0" />
|
||||
<PackageReference Include="Fake.IO.FileSystem" Version="5.23.0" />
|
||||
<PackageReference Include="Farmer" Version="1.7.10" />
|
||||
<PackageReference Update="FSharp.Core" Version="6.0.5" />
|
||||
<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>
|
||||
@@ -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
16
Oceanbox.FvcomKit.slnx
Normal 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>
|
||||
281
RELEASE_NOTES.md
281
RELEASE_NOTES.md
@@ -1,5 +1,286 @@
|
||||
# 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)
|
||||
|
||||
|
||||
|
||||
302
bun.lock
Normal file
302
bun.lock
Normal 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
19
default.nix
Normal 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
146
nix/default.nix
Normal 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
29
nix/projnet.fsharp.nix
Normal 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
29
nix/sdslite.nix
Normal 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
23
nix/sources.json
Normal 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
4
nix/sources.nix
Normal 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";
|
||||
}
|
||||
2772
package-lock.json
generated
2772
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
25
shell.nix
Normal file
25
shell.nix
Normal 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;
|
||||
}
|
||||
236
src/Adjoin.fs
236
src/Adjoin.fs
@@ -1,7 +1,7 @@
|
||||
module Oceanbox.FvcomKit.Adjoin
|
||||
|
||||
open FSharpPlus
|
||||
open FsKDTree
|
||||
//open FsKDTree
|
||||
open ProjNet.FSharp
|
||||
open Grid
|
||||
open Types
|
||||
@@ -9,10 +9,10 @@ open Polygon
|
||||
open KdTree // C# version
|
||||
|
||||
type CropBox = (float * float) * (float * float)
|
||||
type Mask = bool [,]
|
||||
type Mask = bool[,]
|
||||
type Pos = float * float
|
||||
type PosVec = Pos []
|
||||
type BiPos = float [,] * float [,]
|
||||
type PosVec = Pos[]
|
||||
type BiPos = float[,] * float[,]
|
||||
|
||||
let private reProject (proj: IProj) ((lng, lat): BiPos) =
|
||||
lng
|
||||
@@ -20,7 +20,7 @@ let private reProject (proj: IProj) ((lng, lat): BiPos) =
|
||||
|> Array2D.map proj.project
|
||||
|> unzip2D
|
||||
|
||||
let private genCropIdx ((min, max): float * float) (m: float [,]) =
|
||||
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
|
||||
@@ -44,22 +44,21 @@ let private mkCropBox (box: BBox) : CropBox =
|
||||
|
||||
// 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])
|
||||
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 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<float, int> []) =
|
||||
let tree = KdTree<float, int>(2, KdTree.Math.DoubleMath())
|
||||
let private createTree (points: Leaf<int>[]) =
|
||||
let tree = KdTree<float, int> (2, KdTree.Math.DoubleMath ())
|
||||
points
|
||||
|> Array.iter (fun a -> tree.Add([| a.Pos.X; a.Pos.Y |], a.Data) |> ignore)
|
||||
|> Array.iter (fun a -> tree.Add ([| fst a.Pos; snd a.Pos |], a.Data) |> ignore)
|
||||
if points.Length > 0 then
|
||||
tree.Balance()
|
||||
tree.Balance ()
|
||||
else
|
||||
// Log.Warning $"Empty kd-tree"
|
||||
()
|
||||
@@ -68,14 +67,14 @@ let private createTree (points: Leaf<float, int> []) =
|
||||
// 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 = x; Y = y }; Data = n })
|
||||
|> 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 [] }
|
||||
type FvcomAdjoint = { adjoinIdx: int[]; cullIdx: (int * int)[]; oobIdx: int[] }
|
||||
|
||||
let private dist (a: float * float) (b: float * float) =
|
||||
let x0, y0 = a
|
||||
@@ -107,14 +106,13 @@ let private distLngLat (proj: IProj) (a: float * float) (b: float * float) =
|
||||
// (0, [])
|
||||
// |> snd
|
||||
// |> Array.ofList
|
||||
let private genOobIdx (proj: IProj) (tree: KdTree<_, _>) (cullIdx: (int * int) []) (fPos: PosVec) (rPos: BiPos) =
|
||||
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])
|
||||
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)
|
||||
tree.GetNearestNeighbours ([| x; y |], 1)
|
||||
|> Array.head
|
||||
|> fun p ->
|
||||
let i0, i1 = cullIdx[p.Value]
|
||||
@@ -141,6 +139,7 @@ let private genOobIdx (proj: IProj) (tree: KdTree<_, _>) (cullIdx: (int * int) [
|
||||
// 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
|
||||
@@ -148,15 +147,10 @@ let private mkFvcomAdjoint (proj: IProj) (fPos: PosVec) (bbox: BBox) ((rPos, wet
|
||||
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)
|
||||
{
|
||||
adjoinIdx = nearest
|
||||
cullIdx = cullIdx
|
||||
oobIdx = genOobIdx proj tree cullIdx fPos rPos
|
||||
}
|
||||
|> 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
|
||||
|
||||
@@ -169,26 +163,26 @@ 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<float, int * int> []) =
|
||||
let tree = KdTree<float, int * int>(2, KdTree.Math.DoubleMath())
|
||||
let private createIdxTree (points: Leaf<int * int>[]) =
|
||||
let tree = KdTree<float, int * int> (2, KdTree.Math.DoubleMath ())
|
||||
points
|
||||
|> Array.iter (fun a -> tree.Add([| a.Pos.X; a.Pos.Y |], a.Data) |> ignore)
|
||||
|> Array.iter (fun a -> tree.Add ([| fst a.Pos; snd a.Pos |], a.Data) |> ignore)
|
||||
if points.Length > 0 then
|
||||
tree.Balance()
|
||||
tree.Balance ()
|
||||
else
|
||||
//Log.Warning $"Empty kd-tree"
|
||||
()
|
||||
tree
|
||||
|
||||
// rPos must be in the right projection!
|
||||
let makeNestTree ((lng, lat): float [,] * float [,]) =
|
||||
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 = x; Y = y }; Data = (i, j) }))
|
||||
{ Pos = x, y; Data = (i, j) }))
|
||||
|> Array.concat
|
||||
// |> create2DTree treeLeafSize
|
||||
|> createIdxTree
|
||||
@@ -196,36 +190,31 @@ let makeNestTree ((lng, lat): float [,] * float [,]) =
|
||||
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
|
||||
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 |]
|
||||
@@ -237,85 +226,84 @@ let getNearestUpperLeft' (tree: KdTree<float, int * int>) (((ew, ns), mask): BiP
|
||||
[| 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
|
||||
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 getNearestUpperLeft
|
||||
(tree: Tree<Leaf<float, int * int> array, 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
|
||||
// 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 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) []) =
|
||||
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) =
|
||||
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 [,]) =
|
||||
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 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
260
src/Arome.fs
Normal 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
|
||||
@@ -1,100 +0,0 @@
|
||||
module Oceanbox.FvcomKit.CloughTocher
|
||||
|
||||
open Grid
|
||||
|
||||
// Cyclic access of array, (A[A.Length] = A[0]), (A[-1] = A[A.Length-1]), etc
|
||||
let private access (A: float []) (idx: int) : float =
|
||||
let l = A.Length
|
||||
let i = idx % l
|
||||
if i >= 0 then A[i] else A[l + i]
|
||||
|
||||
// Clough-Tocher interpolation of 1D field inside float triangular cell
|
||||
// p_0: position to evaluate, should be within the triangle defined by P_n
|
||||
// p_n, f_n, df_n: Positions, values and gradients at cell vertices
|
||||
// Ref: C.L. Lawson "C1-Compatible Interpolation Over a Triangle" (1976)
|
||||
let interpolateTriangleCT (p_n: Pos []) (f_n: float []) (df_n: Field) (p_0: Pos) : float =
|
||||
let idx = [| 0..2 |]
|
||||
|
||||
// Phase 1:
|
||||
let x_n = p_n |> Array.map fst
|
||||
let y_n = p_n |> Array.map snd
|
||||
|
||||
let u =
|
||||
idx
|
||||
|> Array.map (fun i -> (access x_n (i - 1)) - (access x_n (i + 1)))
|
||||
let v =
|
||||
idx
|
||||
|> Array.map (fun i -> (access y_n (i - 1)) - (access y_n (i + 1)))
|
||||
let l2 = Array.map2 (fun a b -> a * a + b * b) u v
|
||||
|
||||
let delta = u[0] * v[1] - v[0] * u[1]
|
||||
if (abs delta) < 1.0e-12 then failwith "Co-linear vertices!"
|
||||
let d_inv = 1.0 / delta
|
||||
|
||||
let x, y = p_0
|
||||
let x_tilde = x - x_n[0]
|
||||
let y_tilde = y - y_n[0]
|
||||
|
||||
let r_1 = d_inv * (u[1] * y_tilde - v[1] * x_tilde)
|
||||
let r_2 = d_inv * (u[2] * y_tilde - v[2] * x_tilde)
|
||||
let r_0 = 1.0 - (r_1 + r_2)
|
||||
let r = [| r_0; r_1; r_2 |]
|
||||
|
||||
let exterior = r |> Array.exists (fun r_i -> r_i < 0.0)
|
||||
if exterior then failwith "Point is outside the triangle!"
|
||||
|
||||
let phi =
|
||||
idx
|
||||
|> Array.map (fun i -> (access r (i + 1)) * (access r (i - 1)))
|
||||
|
||||
// Phase 2 (Zienkievicz):
|
||||
let rho =
|
||||
idx
|
||||
|> Array.map (fun i ->
|
||||
let r_im1 = access r (i - 1)
|
||||
let r_ip1 = access r (i + 1)
|
||||
(r[i] * r_ip1 * r_ip1 * r_im1 * r_im1)
|
||||
/ ((1.0 - r_ip1) * (1.0 - r_im1)))
|
||||
|
||||
// Phase 3:
|
||||
let fx_n = df_n |> Array.map fst
|
||||
let fy_n = df_n |> Array.map snd
|
||||
|
||||
let h_tilde =
|
||||
idx
|
||||
|> Array.map (fun i ->
|
||||
let fx_ip1 = access fx_n (i + 1)
|
||||
let fy_ip1 = access fy_n (i + 1)
|
||||
u[i] * fx_ip1 + v[i] * fy_ip1)
|
||||
|
||||
let k_tilde =
|
||||
idx
|
||||
|> Array.map (fun i ->
|
||||
let fx_im1 = access fx_n (i - 1)
|
||||
let fy_im1 = access fy_n (i - 1)
|
||||
u[i] * fx_im1 + v[i] * fy_im1)
|
||||
|
||||
let g_tilde =
|
||||
idx
|
||||
|> Array.map (fun i ->
|
||||
let r_ip1 = access r (i + 1)
|
||||
let r_im1 = access r (i - 1)
|
||||
let l2_ip1 = access l2 (i + 1)
|
||||
let l2_im1 = access l2 (i - 1)
|
||||
let rho_ip1 = access rho (i + 1)
|
||||
let rho_im1 = access rho (i - 1)
|
||||
(r_ip1 - r_im1) * phi[i]
|
||||
+ (3.0 * rho[i]) * (l2_ip1 - l2_im1) / l2[i]
|
||||
- rho_ip1
|
||||
+ rho_im1)
|
||||
|
||||
let w =
|
||||
idx
|
||||
|> Array.map (fun i ->
|
||||
let f_im1 = access f_n (i - 1)
|
||||
let f_ip1 = access f_n (i + 1)
|
||||
f_n[i] * r[i]
|
||||
+ 0.5 * (h_tilde[i] - k_tilde[i]) * phi[i]
|
||||
+ g_tilde[i]
|
||||
* (0.5 * (h_tilde[i] + k_tilde[i]) - f_im1 + f_ip1))
|
||||
w |> Array.sum
|
||||
@@ -1,332 +0,0 @@
|
||||
module Oceanbox.FvcomKit.Evaluate2D
|
||||
|
||||
open Grid
|
||||
open Gradient
|
||||
open CloughTocher
|
||||
open EvaluateVertex
|
||||
|
||||
let private (<*>) h (x: float, y: float) = (h * x, h * y)
|
||||
let private (<.>) (ax, ay) (bx, by) = (ax * bx + ay * by)
|
||||
let private (<+>) (ax, ay) (bx, by) = (ax + bx, ay + by)
|
||||
let private (<->) (ax, ay) (bx, by) = (ax - bx, ay - by)
|
||||
let private (><) (ax, ay) (bx, by) = (ax * by - ay * bx)
|
||||
|
||||
let private toArray3 (a, b, c) = [| a; b; c |]
|
||||
|
||||
let private getCell (grid: ExtendedGrid) (idx: ElemIdx) = (grid :> IGrid).getCell idx
|
||||
let private getVertex (grid: ExtendedGrid) (idx: NodeIdx) = (grid :> IGrid).getVertex idx
|
||||
let private getCentroid (grid: ExtendedGrid) (idx: ElemIdx) = (grid.getCentroids ())[idx]
|
||||
|
||||
let private containsIndex (idx: NodeIdx) (cell: Cell) =
|
||||
let x, y, z = cell
|
||||
(idx = x) || (idx = y) || (idx = z)
|
||||
|
||||
let private countIndex (idx: NodeIdx) (cell: Cell) =
|
||||
match (containsIndex idx cell) with
|
||||
| true -> 1
|
||||
| false -> 0
|
||||
|
||||
let private countCommonIndices (c1: Cell) (c2: Cell) =
|
||||
let i0, i1, i2 = c1
|
||||
let s0 = countIndex i0 c2
|
||||
let s1 = countIndex i1 c2
|
||||
let s2 = countIndex i2 c2
|
||||
s0 + s1 + s2
|
||||
|
||||
// Elements are adjacent if they have a shared edge, i.e. have two common node indices
|
||||
let private areAdjacent (grid: ExtendedGrid) (i1: ElemIdx) (i0: ElemIdx) =
|
||||
let nCommon =
|
||||
countCommonIndices
|
||||
<| (getCell grid i0)
|
||||
<| (getCell grid i1)
|
||||
nCommon = 2
|
||||
|
||||
// Picks out the three neighbors of an element that are sharing a common edge
|
||||
// (two neighbors for boundary elements)
|
||||
let private getAdjacentNeighbors (grid: ExtendedGrid) (idx: ElemIdx) =
|
||||
let neighbors =
|
||||
grid.getElemsSurroundingElem idx
|
||||
|> Array.filter (areAdjacent grid idx)
|
||||
|
||||
// a valid centroid grid should always give two or three neighbors
|
||||
match neighbors.Length with
|
||||
| n when n < 2 -> failwith "Too few neighbors"
|
||||
| n when n > 3 -> failwith "Too many neighbors"
|
||||
| _ -> neighbors
|
||||
|
||||
let private isEdgeBoundary (grid: ExtendedGrid) (idx: ElemIdx) =
|
||||
let neighbors =
|
||||
grid.getElemsSurroundingElem idx
|
||||
|> Array.filter (areAdjacent grid idx)
|
||||
neighbors.Length = 2
|
||||
|
||||
let private isPointBoundary (grid: ExtendedGrid) (idx: ElemIdx) =
|
||||
let neighbors =
|
||||
grid.getElemsSurroundingElem idx
|
||||
|> Array.filter (areAdjacent grid idx)
|
||||
|
||||
match neighbors.Length with
|
||||
| 2 -> false
|
||||
| 3 ->
|
||||
let neighboundaries = neighbors |> Array.filter (isEdgeBoundary grid)
|
||||
neighboundaries.Length > 0
|
||||
| _ -> failwith "Invalid neighbor length"
|
||||
|
||||
let private getEdgeBoundaryVertices (grid: ExtendedGrid) (e: ElemIdx) : bool [] =
|
||||
let n =
|
||||
grid.getElemsSurroundingElem e
|
||||
|> Array.filter (areAdjacent grid e)
|
||||
match n.Length with
|
||||
| 2 ->
|
||||
let n0 = getCell grid n[0]
|
||||
let n1 = getCell grid n[1]
|
||||
let cell = (getCell grid e) |> toArray3
|
||||
cell
|
||||
|> Array.map (fun ci ->
|
||||
(not (containsIndex ci n0)
|
||||
|| not (containsIndex ci n1)))
|
||||
| _ -> failwith "Invalid number of edge boundary neighbors"
|
||||
|
||||
let private getEdges (grid: ExtendedGrid) (eIdx: ElemIdx) : Edge [] =
|
||||
let c0, c1, c2 = getCell grid eIdx
|
||||
[| (c0, c1); (c1, c2); (c2, c0) |]
|
||||
|
||||
let private pairEdge (e1: Edge) (e2: Edge) = (fst e1 = snd e2) && (snd e1 = fst e2)
|
||||
|
||||
let private sameEdge ((e11, e12): Edge) ((e21, e22): Edge) =
|
||||
(min e11 e12) = (min e21 e22)
|
||||
&& (max e11 e12) = (max e21 e22)
|
||||
|
||||
let rec private containsPair (edges: Edge list) (e: Edge) =
|
||||
match edges with
|
||||
| [] -> false
|
||||
| x :: xs -> if pairEdge e x then true else containsPair xs e
|
||||
|
||||
let rec private containsSame (edges: Edge list) (e: Edge) =
|
||||
match edges with
|
||||
| [] -> false
|
||||
| x :: xs -> if sameEdge e x then true else containsSame xs e
|
||||
|
||||
let private keepBoundary (edges: Edge list) =
|
||||
let rec loop O Y X =
|
||||
match X with
|
||||
| [] -> O
|
||||
| x :: xs ->
|
||||
if (containsPair Y x) then
|
||||
loop O Y xs
|
||||
else
|
||||
loop (x :: O) Y xs
|
||||
loop [] edges edges
|
||||
|
||||
let private commonEdges (e1: Edge list) (e2: Edge list) =
|
||||
let rec loop O Y X =
|
||||
match X with
|
||||
| [] -> O
|
||||
| x :: xs ->
|
||||
if (containsSame Y x) then
|
||||
loop (x :: O) Y xs
|
||||
else
|
||||
loop O Y xs
|
||||
loop [] e1 e2
|
||||
|
||||
let private toNodeList (edges: Edge list) =
|
||||
let rec append O X =
|
||||
match X with
|
||||
| [] -> O
|
||||
| (x1, x2) :: xs -> append (x1 :: x2 :: O) xs
|
||||
append [] edges
|
||||
|
||||
let private onGlobalBoundary (grid: ExtendedGrid) (idx: NodeIdx) : bool =
|
||||
grid.getElemsSurroundingNode idx
|
||||
|> Array.map (getEdges grid)
|
||||
|> Array.reduce Array.append
|
||||
|> Array.toList
|
||||
|> keepBoundary
|
||||
|> toNodeList
|
||||
|> List.contains idx
|
||||
|
||||
let private contains (idx: NodeIdx) (edge: Edge) : bool = fst edge = idx || snd edge = idx
|
||||
|
||||
let private projectOnEdge (grid: ExtendedGrid) (v: float * float) (edge: Edge) =
|
||||
let n0, n1 = edge
|
||||
let x0, y0 = getVertex grid n0
|
||||
let x1, y1 = getVertex grid n1
|
||||
let u = (x1 - x0, y1 - y0)
|
||||
|
||||
// Project only if pointing outward
|
||||
if (v >< u) < 0.0 then (v <.> u) / (u <.> u) <*> u else v
|
||||
|
||||
let private divertOnEdge (grid: ExtendedGrid) (v: float * float) (edge: Edge) =
|
||||
let n0, n1 = edge
|
||||
let x0, y0 = getVertex grid n0
|
||||
let x1, y1 = getVertex grid n1
|
||||
let u = (x1 - x0, y1 - y0)
|
||||
|
||||
// Project only if pointing outward
|
||||
if (v >< u) < 0.0 then
|
||||
let sign = (u <.> v) / abs (u <.> v)
|
||||
sign * sqrt ((v <.> v) / (u <.> u)) <*> u
|
||||
else
|
||||
v
|
||||
|
||||
let private projectOnBoundary (grid: ExtendedGrid) (eIdx: ElemIdx) (nIdx: NodeIdx, v: float * float) =
|
||||
let boundaries =
|
||||
grid.getElemsSurroundingElem eIdx
|
||||
|> Array.map (getEdges grid)
|
||||
|> Array.reduce Array.append
|
||||
|> Array.toList
|
||||
|> keepBoundary
|
||||
|> commonEdges (getEdges grid eIdx |> Array.toList)
|
||||
|
||||
match boundaries.Length with
|
||||
| 0 -> v
|
||||
| 1 ->
|
||||
let b = boundaries[0]
|
||||
if contains nIdx b then projectOnEdge grid v b else v
|
||||
| _ -> failwith "Too many boundary edges"
|
||||
|
||||
let private divertOnBoundary (grid: ExtendedGrid) (eIdx: ElemIdx) (nIdx: NodeIdx, v: float * float) =
|
||||
let boundaries =
|
||||
grid.getElemsSurroundingElem eIdx
|
||||
|> Array.map (getEdges grid)
|
||||
|> Array.reduce Array.append
|
||||
|> Array.toList
|
||||
|> keepBoundary
|
||||
|> commonEdges (getEdges grid eIdx |> Array.toList)
|
||||
|
||||
match boundaries.Length with
|
||||
| 0 -> v
|
||||
| 1 ->
|
||||
let b = boundaries[0]
|
||||
if contains nIdx b then divertOnEdge grid v b else v
|
||||
| _ -> failwith "Too many boundary edges"
|
||||
|
||||
let private projectOnNeighboundaries (grid: ExtendedGrid) (idx: NodeIdx, v: float * float) =
|
||||
let neighboundaries =
|
||||
grid.getElemsSurroundingNode idx
|
||||
|> Array.map (getEdges grid)
|
||||
|> Array.reduce Array.append
|
||||
|> Array.toList
|
||||
|> keepBoundary
|
||||
|> List.filter (contains idx)
|
||||
|
||||
let v_proj = neighboundaries |> List.map (projectOnEdge grid v)
|
||||
match v_proj.Length with
|
||||
| 0 -> v
|
||||
| 1 -> v_proj[0]
|
||||
| 2 -> 0.5 <*> (v_proj[0] <+> v_proj[1])
|
||||
| _ -> failwith "Too many boundary edges"
|
||||
|
||||
let private divertOnNeighboundaries (grid: ExtendedGrid) (idx: NodeIdx, v: float * float) =
|
||||
let neighboundaries =
|
||||
grid.getElemsSurroundingNode idx
|
||||
|> Array.map (getEdges grid)
|
||||
|> Array.reduce Array.append
|
||||
|> Array.toList
|
||||
|> keepBoundary
|
||||
|> List.filter (contains idx)
|
||||
|
||||
let v_proj = neighboundaries |> List.map (divertOnEdge grid v)
|
||||
match v_proj.Length with
|
||||
| 0 -> v
|
||||
| 1 -> v_proj[0]
|
||||
| 2 -> 0.5 <*> (v_proj[0] <+> v_proj[1])
|
||||
| _ -> failwith "Too many boundary edges"
|
||||
|
||||
let private adjustBoundary (grid: ExtendedGrid) (e: ElemIdx) (uv_n: (float * float) []) =
|
||||
let idx_n = (getCell grid e) |> toArray3
|
||||
if (isEdgeBoundary grid e) then
|
||||
(Array.zip idx_n uv_n)
|
||||
|> Array.map (divertOnBoundary grid e)
|
||||
elif (isPointBoundary grid e) then
|
||||
(Array.zip idx_n uv_n)
|
||||
|> Array.map (divertOnNeighboundaries grid)
|
||||
else
|
||||
uv_n
|
||||
|
||||
// Evaluate 2D field based on constant extrapolation from centroid field value
|
||||
let evaluateConstantCentroid2D (grid: ExtendedGrid) (readUV: ElemIdx -> float * float) ((px, py): Pos) =
|
||||
match grid.tryGetElement (px, py) with
|
||||
| None -> None
|
||||
| Some e -> Some(readUV e)
|
||||
|
||||
// Evaluate 2D field based on linear extrapolation from centroid field value,
|
||||
// using gradient estimated from values in the three adjacent neigboring elements
|
||||
let evaluateLinearCentroid2D (grid: ExtendedGrid) (readUV: ElemIdx -> float * float) ((px, py): Pos) =
|
||||
match grid.tryGetElement (px, py) with
|
||||
| None -> None
|
||||
| Some e ->
|
||||
let centroids = grid.getCentroids ()
|
||||
|
||||
let c0 = centroids[e]
|
||||
let u0 = readUV e |> fst
|
||||
let v0 = readUV e |> snd
|
||||
|
||||
let neighbors = getAdjacentNeighbors grid e
|
||||
let cn = neighbors |> Array.map (Array.get centroids)
|
||||
let un =
|
||||
neighbors
|
||||
|> Array.map (fun en -> readUV en |> fst)
|
||||
let vn =
|
||||
neighbors
|
||||
|> Array.map (fun en -> readUV en |> snd)
|
||||
|
||||
let thrs = 1.0e-3
|
||||
let du_x, du_y = calcGradientFromNeighbors c0 u0 cn un thrs
|
||||
let dv_x, dv_y = calcGradientFromNeighbors c0 v0 cn vn thrs
|
||||
|
||||
let x, y = px, py
|
||||
let x0, y0 = c0
|
||||
|
||||
let u = u0 + du_x * (x - x0) + du_y * (y - y0)
|
||||
let v = v0 + dv_x * (x - x0) + dv_y * (y - y0)
|
||||
Some(u, v)
|
||||
|
||||
// Evaluate 2D field based on Clough-Tocher interpolation within grid element
|
||||
let evaluateCloughTocherCentroid2D (grid: ExtendedGrid) (readUV: ElemIdx -> float * float) ((px, py): Pos) =
|
||||
match grid.tryGetElement (px, py) with
|
||||
| None -> None
|
||||
| Some e ->
|
||||
let idx = (getCell grid e) |> toArray3
|
||||
|
||||
let readU = fst << readUV
|
||||
let readV = snd << readUV
|
||||
|
||||
// Position of vertices
|
||||
let p_n = idx |> Array.map (getVertex grid)
|
||||
|
||||
// Field values at vertices
|
||||
let u_n = idx |> Array.map (calcVertexValue1D grid readU)
|
||||
let v_n = idx |> Array.map (calcVertexValue1D grid readV)
|
||||
|
||||
// Enforce boundary conditions
|
||||
let uv_n = Array.zip u_n v_n
|
||||
let uv_n' = adjustBoundary grid e uv_n
|
||||
let u_n' = uv_n' |> Array.map fst
|
||||
let v_n' = uv_n' |> Array.map snd
|
||||
|
||||
// Gradient values at vertices
|
||||
let thrs = 1.0e-3
|
||||
let du_n =
|
||||
(Array.zip idx u_n')
|
||||
|> Array.map (calcVertexGradient grid readU thrs)
|
||||
let dv_n =
|
||||
(Array.zip idx v_n')
|
||||
|> Array.map (calcVertexGradient grid readV thrs)
|
||||
|
||||
// Rearrange to x and y derivatives: [grad(U), grad(V)] -> [dx(UV), dy(UV)]
|
||||
let dx_uv_n = Array.zip (Array.map fst du_n) (Array.map fst dv_n)
|
||||
let dy_uv_n = Array.zip (Array.map snd du_n) (Array.map snd dv_n)
|
||||
|
||||
// Enforce boundary conditions
|
||||
let dx_uv_n' = adjustBoundary grid e dx_uv_n
|
||||
let dy_uv_n' = adjustBoundary grid e dy_uv_n
|
||||
|
||||
// Rearrange back to gradients: [dx(UV), dy(UV)] -> [grad(U), grad(V)]
|
||||
let du_n' = Array.zip (Array.map fst dx_uv_n') (Array.map fst dy_uv_n')
|
||||
let dv_n' = Array.zip (Array.map snd dx_uv_n') (Array.map snd dy_uv_n')
|
||||
|
||||
// Perform Clough-Tocher interpolation
|
||||
let u1 = interpolateTriangleCT p_n u_n' du_n' (px, py)
|
||||
let v1 = interpolateTriangleCT p_n v_n' dv_n' (px, py)
|
||||
Some(u1, v1)
|
||||
@@ -1,496 +0,0 @@
|
||||
module Oceanbox.FvcomKit.Evaluate4D
|
||||
|
||||
open Grid
|
||||
open Gradient
|
||||
open CloughTocher
|
||||
open EvaluateVertex
|
||||
|
||||
let private (<*>) h (x: float, y: float, z: float) = (h * x, h * y, h * z)
|
||||
let private (<.>) (ax, ay, az) (bx, by, bz) = (ax * bx + ay * by + az * bz)
|
||||
let private (<+>) (ax, ay, az) (bx, by, bz) = (ax + bx, ay + by, az + bz)
|
||||
let private (<->) (ax, ay, az) (bx, by, bz) = (ax - bx, ay - by, az - bz)
|
||||
|
||||
let private (><) (ax, ay, az) (bx, by, bz) =
|
||||
(ay * bz - az * by, az * bx - ax * bz, ax * by - ay * bx)
|
||||
|
||||
let private fst3 (a, _, _) = a
|
||||
let private snd3 (_, b, _) = b
|
||||
let private trd3 (_, _, c) = c
|
||||
let private toArray3 (a, b, c) = [| a; b; c |]
|
||||
|
||||
let private getCell (grid: ExtendedGrid) (idx: ElemIdx) = (grid :> IGrid).getCell idx
|
||||
let private getVertex (grid: ExtendedGrid) (idx: NodeIdx) = (grid :> IGrid).getVertex idx
|
||||
let private getCentroid (grid: ExtendedGrid) (idx: ElemIdx) = (grid.getCentroids ())[idx]
|
||||
|
||||
let private containsIndex (idx: NodeIdx) (cell: Cell) =
|
||||
let x, y, z = cell
|
||||
(idx = x) || (idx = y) || (idx = z)
|
||||
|
||||
let private countIndex (idx: NodeIdx) (cell: Cell) =
|
||||
match (containsIndex idx cell) with
|
||||
| true -> 1
|
||||
| false -> 0
|
||||
|
||||
let private countCommonIndices (c1: Cell) (c2: Cell) =
|
||||
let i0, i1, i2 = c1
|
||||
let s0 = countIndex i0 c2
|
||||
let s1 = countIndex i1 c2
|
||||
let s2 = countIndex i2 c2
|
||||
s0 + s1 + s2
|
||||
|
||||
// Elements are adjacent if they have a shared edge, i.e. have two common node indices
|
||||
let private areAdjacent (grid: ExtendedGrid) (i1: ElemIdx) (i0: ElemIdx) =
|
||||
let nCommon =
|
||||
countCommonIndices
|
||||
<| (getCell grid i0)
|
||||
<| (getCell grid i1)
|
||||
nCommon = 2
|
||||
|
||||
// Picks out the three neighbors of an element that are sharing a common edge
|
||||
// (two neighbors for boundary elements)
|
||||
let private getAdjacentNeighbors (grid: ExtendedGrid) (idx: ElemIdx) =
|
||||
let neighbors =
|
||||
grid.getElemsSurroundingElem idx
|
||||
|> Array.filter (areAdjacent grid idx)
|
||||
|
||||
// a valid centroid grid should always give two or three neighbors
|
||||
match neighbors.Length with
|
||||
| n when n < 2 -> failwith "Too few neighbors"
|
||||
| n when n > 3 -> failwith "Too many neighbors"
|
||||
| _ -> neighbors
|
||||
|
||||
let private isEdgeBoundary (grid: ExtendedGrid) (idx: ElemIdx) =
|
||||
let neighbors =
|
||||
grid.getElemsSurroundingElem idx
|
||||
|> Array.filter (areAdjacent grid idx)
|
||||
neighbors.Length = 2
|
||||
|
||||
let private isPointBoundary (grid: ExtendedGrid) (idx: ElemIdx) =
|
||||
let neighbors =
|
||||
grid.getElemsSurroundingElem idx
|
||||
|> Array.filter (areAdjacent grid idx)
|
||||
|
||||
match neighbors.Length with
|
||||
| 2 -> false
|
||||
| 3 ->
|
||||
let neighboundaries = neighbors |> Array.filter (isEdgeBoundary grid)
|
||||
neighboundaries.Length > 0
|
||||
| _ -> failwith "Invalid neighbor length"
|
||||
|
||||
let private getEdgeBoundaryVertices (grid: ExtendedGrid) (e: ElemIdx) : bool [] =
|
||||
let n =
|
||||
grid.getElemsSurroundingElem e
|
||||
|> Array.filter (areAdjacent grid e)
|
||||
match n.Length with
|
||||
| 2 ->
|
||||
let n0 = getCell grid n[0]
|
||||
let n1 = getCell grid n[1]
|
||||
let cell = (getCell grid e) |> toArray3
|
||||
cell
|
||||
|> Array.map (fun ci ->
|
||||
(not (containsIndex ci n0)
|
||||
|| not (containsIndex ci n1)))
|
||||
| _ -> failwith "Invalid number of edge boundary neighbors"
|
||||
|
||||
let private getEdges (grid: ExtendedGrid) (eIdx: ElemIdx) : Edge [] =
|
||||
let c0, c1, c2 = getCell grid eIdx
|
||||
[| (c0, c1); (c1, c2); (c2, c0) |]
|
||||
|
||||
let private pairEdge (e1: Edge) (e2: Edge) = (fst e1 = snd e2) && (snd e1 = fst e2)
|
||||
|
||||
let private sameEdge ((e11, e12): Edge) ((e21, e22): Edge) =
|
||||
(min e11 e12) = (min e21 e22)
|
||||
&& (max e11 e12) = (max e21 e22)
|
||||
|
||||
let rec private containsPair (edges: Edge list) (e: Edge) =
|
||||
match edges with
|
||||
| [] -> false
|
||||
| x :: xs -> if pairEdge e x then true else containsPair xs e
|
||||
|
||||
let rec private containsSame (edges: Edge list) (e: Edge) =
|
||||
match edges with
|
||||
| [] -> false
|
||||
| x :: xs -> if sameEdge e x then true else containsSame xs e
|
||||
|
||||
let private keepBoundary (edges: Edge list) =
|
||||
let rec loop O Y X =
|
||||
match X with
|
||||
| [] -> O
|
||||
| x :: xs ->
|
||||
if (containsPair Y x) then
|
||||
loop O Y xs
|
||||
else
|
||||
loop (x :: O) Y xs
|
||||
loop [] edges edges
|
||||
|
||||
let private commonEdges (e1: Edge list) (e2: Edge list) =
|
||||
let rec loop O Y X =
|
||||
match X with
|
||||
| [] -> O
|
||||
| x :: xs ->
|
||||
if (containsSame Y x) then
|
||||
loop (x :: O) Y xs
|
||||
else
|
||||
loop O Y xs
|
||||
loop [] e1 e2
|
||||
|
||||
let private toNodeList (edges: Edge list) =
|
||||
let rec append O X =
|
||||
match X with
|
||||
| [] -> O
|
||||
| (x1, x2) :: xs -> append (x1 :: x2 :: O) xs
|
||||
append [] edges
|
||||
|
||||
let private onGlobalBoundary (grid: ExtendedGrid) (idx: NodeIdx) : bool =
|
||||
grid.getElemsSurroundingNode idx
|
||||
|> Array.map (getEdges grid)
|
||||
|> Array.reduce Array.append
|
||||
|> Array.toList
|
||||
|> keepBoundary
|
||||
|> toNodeList
|
||||
|> List.contains idx
|
||||
|
||||
let private contains (idx: NodeIdx) (edge: Edge) : bool = fst edge = idx || snd edge = idx
|
||||
|
||||
let private projectOnEdge (grid: ExtendedGrid) (v: float * float * float) (edge: Edge) : float * float * float =
|
||||
let n0, n1 = edge
|
||||
let x0, y0 = getVertex grid n0
|
||||
let x1, y1 = getVertex grid n1
|
||||
let u = (x1 - x0, y1 - y0, 0.0)
|
||||
|
||||
// Project only if pointing outward (negative z-component of cross product)
|
||||
let _, _, k = v >< u
|
||||
if k < 0.0 then (v <.> u) / (u <.> u) <*> u else v
|
||||
|
||||
let private divertOnEdge (grid: ExtendedGrid) (v: float * float * float) (edge: Edge) : float * float * float =
|
||||
let n0, n1 = edge
|
||||
let x0, y0 = getVertex grid n0
|
||||
let x1, y1 = getVertex grid n1
|
||||
let u = (x1 - x0, y1 - y0, 0.0)
|
||||
|
||||
// Project only if pointing outward (negative z-component of cross product)
|
||||
let _, _, k = v >< u
|
||||
if k < 0.0 then
|
||||
let sign = (u <.> v) / abs (u <.> v)
|
||||
sign * sqrt ((v <.> v) / (u <.> u)) <*> u
|
||||
else
|
||||
v
|
||||
|
||||
let private projectOnBoundary
|
||||
(grid: ExtendedGrid)
|
||||
(eIdx: ElemIdx)
|
||||
(nIdx: NodeIdx, v: float * float * float)
|
||||
: float * float * float =
|
||||
let boundaries =
|
||||
grid.getElemsSurroundingElem eIdx
|
||||
|> Array.map (getEdges grid)
|
||||
|> Array.reduce Array.append
|
||||
|> Array.toList
|
||||
|> keepBoundary
|
||||
|> commonEdges (getEdges grid eIdx |> Array.toList)
|
||||
|
||||
match boundaries.Length with
|
||||
| 0 -> v
|
||||
| 1 ->
|
||||
let b = boundaries[0]
|
||||
if contains nIdx b then projectOnEdge grid v b else v
|
||||
| _ -> failwith "Too many boundary edges"
|
||||
|
||||
let private divertOnBoundary
|
||||
(grid: ExtendedGrid)
|
||||
(eIdx: ElemIdx)
|
||||
(nIdx: NodeIdx, v: float * float * float)
|
||||
: float * float * float =
|
||||
let boundaries =
|
||||
grid.getElemsSurroundingElem eIdx
|
||||
|> Array.map (getEdges grid)
|
||||
|> Array.reduce Array.append
|
||||
|> Array.toList
|
||||
|> keepBoundary
|
||||
|> commonEdges (getEdges grid eIdx |> Array.toList)
|
||||
|
||||
match boundaries.Length with
|
||||
| 0 -> v
|
||||
| 1 ->
|
||||
let b = boundaries[0]
|
||||
if contains nIdx b then divertOnEdge grid v b else v
|
||||
| _ -> failwith "Too many boundary edges"
|
||||
|
||||
let private projectOnNeighboundaries
|
||||
(grid: ExtendedGrid)
|
||||
(idx: NodeIdx, v: float * float * float)
|
||||
: float * float * float =
|
||||
let neighboundaries =
|
||||
grid.getElemsSurroundingNode idx
|
||||
|> Array.map (getEdges grid)
|
||||
|> Array.reduce Array.append
|
||||
|> Array.toList
|
||||
|> keepBoundary
|
||||
|> List.filter (contains idx)
|
||||
|
||||
let v_proj = neighboundaries |> List.map (projectOnEdge grid v)
|
||||
match v_proj.Length with
|
||||
| 0 -> v
|
||||
| 1 -> v_proj[0]
|
||||
| 2 -> 0.5 <*> (v_proj[0] <+> v_proj[1])
|
||||
| _ -> failwith "Too many boundary edges"
|
||||
|
||||
let private divertOnNeighboundaries
|
||||
(grid: ExtendedGrid)
|
||||
(idx: NodeIdx, v: float * float * float)
|
||||
: float * float * float =
|
||||
let neighboundaries =
|
||||
grid.getElemsSurroundingNode idx
|
||||
|> Array.map (getEdges grid)
|
||||
|> Array.reduce Array.append
|
||||
|> Array.toList
|
||||
|> keepBoundary
|
||||
|> List.filter (contains idx)
|
||||
|
||||
let v_proj = neighboundaries |> List.map (divertOnEdge grid v)
|
||||
match v_proj.Length with
|
||||
| 0 -> v
|
||||
| 1 -> v_proj[0]
|
||||
| 2 -> 0.5 <*> (v_proj[0] <+> v_proj[1])
|
||||
| _ -> failwith "Too many boundary edges"
|
||||
|
||||
let private adjustBoundary (grid: ExtendedGrid) (e: ElemIdx) (uvw_n: (float * float * float) []) =
|
||||
let idx_n = (getCell grid e) |> toArray3
|
||||
if (isEdgeBoundary grid e) then
|
||||
(Array.zip idx_n uvw_n)
|
||||
|> Array.map (divertOnBoundary grid e)
|
||||
elif (isPointBoundary grid e) then
|
||||
(Array.zip idx_n uvw_n)
|
||||
|> Array.map (divertOnNeighboundaries grid)
|
||||
else
|
||||
uvw_n
|
||||
|
||||
let private calcSigmaLayers (field: Field4D) (sigma: float) : int * int =
|
||||
let slayers = field.Siglays
|
||||
let slayer =
|
||||
slayers
|
||||
|> Array.filter (fun i -> i > sigma)
|
||||
|> Array.length
|
||||
match slayer with
|
||||
| s when s = 0 -> (0, 0)
|
||||
| s when s = slayers.Length -> (s - 1, s - 1)
|
||||
| _ -> (slayer - 1, slayer)
|
||||
|
||||
// Linear interpolation in sigma and time in given cell
|
||||
let private interpolateST (field: Field4D) (s: float) (t: float) (eIdx: ElemIdx) =
|
||||
let slayer0, slayer1 = calcSigmaLayers field s
|
||||
let s0 = field.Siglays[slayer0]
|
||||
let s1 = field.Siglays[slayer1]
|
||||
let t0 = field.Time0
|
||||
let t1 = field.Time1
|
||||
let frame0 = field.Frame0
|
||||
let frame1 = field.Frame1
|
||||
|
||||
// Interpolate f(x) between fi = f(xi) and fj = f(xj)
|
||||
let fx x i j xi xj fi fj =
|
||||
if (i = j) then
|
||||
fi
|
||||
else
|
||||
let a = (x - xi) / (xj - xi)
|
||||
(a * fj) + (1.0 - a) * fi
|
||||
|
||||
let interpolateSigma fi fj = fx s slayer0 slayer1 s0 s1 fi fj
|
||||
let interpolateTime fi fj = fx t frame0 frame1 t0 t1 fi fj
|
||||
|
||||
let u00, v00, w00 = field.Field0[slayer0][eIdx]
|
||||
let u01, v01, w01 = field.Field0[slayer1][eIdx]
|
||||
let u10, v10, w10 = field.Field1[slayer0][eIdx]
|
||||
let u11, v11, w11 = field.Field1[slayer1][eIdx]
|
||||
|
||||
let u0 = interpolateTime u00 u10
|
||||
let u1 = interpolateTime u01 u11
|
||||
let u = interpolateSigma u0 u1
|
||||
|
||||
let v0 = interpolateTime v00 v10
|
||||
let v1 = interpolateTime v01 v11
|
||||
let v = interpolateSigma v0 v1
|
||||
|
||||
let w0 = interpolateTime w00 w10
|
||||
let w1 = interpolateTime w01 w11
|
||||
let w = interpolateSigma w0 w1
|
||||
|
||||
let h = field.Bath[eIdx]
|
||||
(u, v, w / h)
|
||||
|
||||
// Evaluate 4D field based on constant horizontal extrapolation from centroid field value
|
||||
// Linear interpolation in vertical and time
|
||||
let evaluateConstantCentroid4D (grid: ExtendedGrid) (field: Field4D) (t: float) ((x, y, s): Pos3D) =
|
||||
match grid.tryGetElement (x, y) with
|
||||
| None -> None
|
||||
| Some e ->
|
||||
// Set up linear sigma and time interpolation
|
||||
let readUVW eIdx = interpolateST field s t eIdx
|
||||
|
||||
let u = readUVW e |> fst3
|
||||
let v = readUVW e |> snd3
|
||||
let w = readUVW e |> trd3
|
||||
|
||||
Some(u, v, w)
|
||||
|
||||
// Evaluate 4D field based on linear horizontal extrapolation from centroid field value,
|
||||
// using gradient estimated from values in the three adjacent neigboring elements.
|
||||
// Linear interpolation in vertical and time
|
||||
let evaluateLinearCentroid4D (grid: ExtendedGrid) (field: Field4D) (t: float) ((x, y, s): Pos3D) =
|
||||
match grid.tryGetElement (x, y) with
|
||||
| None -> None
|
||||
| Some e ->
|
||||
// Set up linear sigma and time interpolation
|
||||
let readUVW eIdx = interpolateST field s t eIdx
|
||||
|
||||
let centroids = grid.getCentroids ()
|
||||
let c0 = centroids[e]
|
||||
let u0 = readUVW e |> fst3
|
||||
let v0 = readUVW e |> snd3
|
||||
let w0 = readUVW e |> trd3
|
||||
|
||||
let neighbors = getAdjacentNeighbors grid e
|
||||
let cn = neighbors |> Array.map (Array.get centroids)
|
||||
let un =
|
||||
neighbors
|
||||
|> Array.map (fun en -> readUVW en |> fst3)
|
||||
let vn =
|
||||
neighbors
|
||||
|> Array.map (fun en -> readUVW en |> snd3)
|
||||
let wn =
|
||||
neighbors
|
||||
|> Array.map (fun en -> readUVW en |> trd3)
|
||||
|
||||
let thrs = 1.0e-6
|
||||
let dxU, dyU = calcGradientFromNeighbors c0 u0 cn un thrs
|
||||
let dxV, dyV = calcGradientFromNeighbors c0 v0 cn vn thrs
|
||||
let dxW, dyW = calcGradientFromNeighbors c0 w0 cn wn thrs
|
||||
|
||||
let dx = x - (fst c0)
|
||||
let dy = y - (snd c0)
|
||||
|
||||
let u = u0 + dxU * dx + dyU * dy
|
||||
let v = v0 + dxV * dx + dyV * dy
|
||||
let w = w0 + dxW * dx + dyW * dy
|
||||
Some(u, v, w)
|
||||
|
||||
// Evaluate 4D field based on horizontal Clough-Tocher interpolation within grid element
|
||||
// Linear interpolation in vertical and time
|
||||
// Expects field to be given in centroid points
|
||||
let evaluateCloughTocherCentroid4D (grid: ExtendedGrid) (field: Field4D) (t: float) ((x, y, s): Pos3D) =
|
||||
match grid.tryGetElement (x, y) with
|
||||
| None -> None
|
||||
| Some e ->
|
||||
// Set up linear sigma and time interpolation
|
||||
let readUVW eIdx = interpolateST field s t eIdx
|
||||
|
||||
// Position of vertices
|
||||
let idx = (getCell grid e) |> toArray3
|
||||
let p_n = idx |> Array.map (getVertex grid)
|
||||
|
||||
// Field values at vertices
|
||||
let uvw_n = idx |> Array.map (calcVertexValue3D grid readUVW)
|
||||
let uvw_n' = adjustBoundary grid e uvw_n
|
||||
let u_n' = uvw_n' |> Array.map fst3
|
||||
let v_n' = uvw_n' |> Array.map snd3
|
||||
let w_n' = uvw_n' |> Array.map trd3
|
||||
|
||||
let readU = fst3 << readUVW
|
||||
let readV = snd3 << readUVW
|
||||
let readW = trd3 << readUVW
|
||||
|
||||
// Gradient values at vertices
|
||||
let thrs = 1.0e-6
|
||||
let du_n =
|
||||
(Array.zip idx u_n')
|
||||
|> Array.map (calcVertexGradient grid readU thrs)
|
||||
let dv_n =
|
||||
(Array.zip idx v_n')
|
||||
|> Array.map (calcVertexGradient grid readV thrs)
|
||||
let dw_n =
|
||||
(Array.zip idx w_n')
|
||||
|> Array.map (calcVertexGradient grid readW thrs)
|
||||
|
||||
// Rearrange to x and y derivatives: [grad(U), grad(V)] -> [dx(UV), dy(UV)]
|
||||
let dx_uvw_n =
|
||||
Array.zip3 (Array.map fst du_n) (Array.map fst dv_n) (Array.map fst dw_n)
|
||||
let dy_uvw_n =
|
||||
Array.zip3 (Array.map snd du_n) (Array.map snd dv_n) (Array.map snd dw_n)
|
||||
|
||||
// Enforce boundary conditions
|
||||
let dx_uvw_n' = adjustBoundary grid e dx_uvw_n
|
||||
let dy_uvw_n' = adjustBoundary grid e dy_uvw_n
|
||||
|
||||
// Rearrange back to gradients: [dx(UV), dy(UV)] -> [grad(U), grad(V)]
|
||||
let du_n' =
|
||||
Array.zip (Array.map fst3 dx_uvw_n') (Array.map fst3 dy_uvw_n')
|
||||
let dv_n' =
|
||||
Array.zip (Array.map snd3 dx_uvw_n') (Array.map snd3 dy_uvw_n')
|
||||
let dw_n' =
|
||||
Array.zip (Array.map trd3 dx_uvw_n') (Array.map trd3 dy_uvw_n')
|
||||
|
||||
// Perform Clough-Tocher interpolation
|
||||
let u1 = interpolateTriangleCT p_n u_n' du_n' (x, y)
|
||||
let v1 = interpolateTriangleCT p_n v_n' dv_n' (x, y)
|
||||
let w1 = interpolateTriangleCT p_n w_n' dw_n' (x, y)
|
||||
Some(u1, v1, w1)
|
||||
|
||||
|
||||
// Evaluate 4D field based on Clough-Tocher interpolation within grid element
|
||||
// Expects field to be given in vertex points
|
||||
// Linear interpolation in vertical and time
|
||||
let evaluateCloughTocherVertex4D (grid: ExtendedGrid) (field: Field4D) (t: float) ((x, y, s): Pos3D) =
|
||||
match grid.tryGetElement (x, y) with
|
||||
| None -> None
|
||||
| Some e ->
|
||||
// Set up linear sigma and time interpolation
|
||||
let readUVW eIdx = interpolateST field s t eIdx
|
||||
let readU = fst3 << readUVW
|
||||
let readV = snd3 << readUVW
|
||||
let readW = trd3 << readUVW
|
||||
|
||||
// Position of vertices
|
||||
let idx = (getCell grid e) |> toArray3
|
||||
let p_n = idx |> Array.map (getVertex grid)
|
||||
|
||||
// Field values at vertices
|
||||
let uvw_n = idx |> Array.map readUVW
|
||||
let uvw_n' = adjustBoundary grid e uvw_n
|
||||
let u_n' = uvw_n' |> Array.map fst3
|
||||
let v_n' = uvw_n' |> Array.map snd3
|
||||
let w_n' = uvw_n' |> Array.map trd3
|
||||
|
||||
// Gradient values at vertices
|
||||
let thrs = 1.0e-6
|
||||
let du_n =
|
||||
(Array.zip idx u_n')
|
||||
|> Array.map (calcVertexGradient' grid readU thrs)
|
||||
let dv_n =
|
||||
(Array.zip idx v_n')
|
||||
|> Array.map (calcVertexGradient' grid readV thrs)
|
||||
let dw_n =
|
||||
(Array.zip idx w_n')
|
||||
|> Array.map (calcVertexGradient' grid readW thrs)
|
||||
|
||||
// Rearrange to x and y derivatives: [grad(U), grad(V)] -> [dx(UV), dy(UV)]
|
||||
let dx_uvw_n =
|
||||
Array.zip3 (Array.map fst du_n) (Array.map fst dv_n) (Array.map fst dw_n)
|
||||
let dy_uvw_n =
|
||||
Array.zip3 (Array.map snd du_n) (Array.map snd dv_n) (Array.map snd dw_n)
|
||||
|
||||
// Enforce boundary conditions
|
||||
let dx_uvw_n' = adjustBoundary grid e dx_uvw_n
|
||||
let dy_uvw_n' = adjustBoundary grid e dy_uvw_n
|
||||
|
||||
// Rearrange back to gradients: [dx(UV), dy(UV)] -> [grad(U), grad(V)]
|
||||
let du_n' =
|
||||
Array.zip (Array.map fst3 dx_uvw_n') (Array.map fst3 dy_uvw_n')
|
||||
let dv_n' =
|
||||
Array.zip (Array.map snd3 dx_uvw_n') (Array.map snd3 dy_uvw_n')
|
||||
let dw_n' =
|
||||
Array.zip (Array.map trd3 dx_uvw_n') (Array.map trd3 dy_uvw_n')
|
||||
|
||||
// Perform Clough-Tocher interpolation
|
||||
let u1 = interpolateTriangleCT p_n u_n' du_n' (x, y)
|
||||
let v1 = interpolateTriangleCT p_n v_n' dv_n' (x, y)
|
||||
let w1 = interpolateTriangleCT p_n w_n' dw_n' (x, y)
|
||||
Some(u1, v1, w1)
|
||||
@@ -1,109 +0,0 @@
|
||||
module Oceanbox.FvcomKit.EvaluateVertex
|
||||
|
||||
open Grid
|
||||
|
||||
let private (<->) (ax, ay) (bx, by) = (ax - bx, ay - by)
|
||||
let private inv_norm (px, py) = 1.0 / sqrt (px * px + py * py)
|
||||
|
||||
let private fst3 (u, _, _) = u
|
||||
let private snd3 (_, v, _) = v
|
||||
let private trd3 (_, _, w) = w
|
||||
|
||||
let private getVertex (grid: ExtendedGrid) (idx: NodeIdx) = (grid :> IGrid).getVertex idx
|
||||
let private getCentroid (grid: ExtendedGrid) (idx: ElemIdx) = (grid.getCentroids ())[idx]
|
||||
|
||||
let calcVertexValue1D (grid: ExtendedGrid) (readU: ElemIdx -> float) (idx: NodeIdx) : float =
|
||||
// Fetch surrounding elements
|
||||
let e_n = grid.getElemsSurroundingNode idx
|
||||
|
||||
// Fetch points
|
||||
let p_0 = getVertex grid idx
|
||||
let p_n = e_n |> Array.map (getCentroid grid)
|
||||
|
||||
// Fetch function values
|
||||
let f_n = e_n |> Array.map readU
|
||||
|
||||
// Compute inverse distance weights
|
||||
let w_n =
|
||||
p_n
|
||||
|> Array.map (fun p -> p <-> p_0)
|
||||
|> Array.map inv_norm
|
||||
let W = w_n |> Array.sum
|
||||
|
||||
// Compute mean function value on vertex P_0
|
||||
(Array.map2 (*) w_n f_n |> Array.sum) / W
|
||||
|
||||
let centroidToVertexValues1D (grid: ExtendedGrid) (readU: ElemIdx -> float) : float [] =
|
||||
let N = ((grid :> IGrid).getVertices ()).Length
|
||||
[| 0 .. (N - 1) |]
|
||||
|> Array.Parallel.map (calcVertexValue1D grid readU)
|
||||
|
||||
|
||||
|
||||
// Given 2D centroid evaluator "readUV", compute field value in single vertex
|
||||
// by a weighted average of values in all surrounding elements (within sigma layer)
|
||||
let calcVertexValue2D (grid: ExtendedGrid) (readUV: ElemIdx -> float * float) (idx: NodeIdx) =
|
||||
// Fetch surrounding elements
|
||||
let e_n = grid.getElemsSurroundingNode idx
|
||||
|
||||
// Fetch points
|
||||
let p_0 = getVertex grid idx
|
||||
let p_n = e_n |> Array.map (getCentroid grid)
|
||||
|
||||
// Fetch function values
|
||||
let f_n = e_n |> Array.map readUV
|
||||
let u_n = f_n |> Array.map fst
|
||||
let v_n = f_n |> Array.map snd
|
||||
|
||||
// Compute inverse distance weights
|
||||
let wgt_n =
|
||||
p_n
|
||||
|> Array.map (fun p -> p <-> p_0)
|
||||
|> Array.map inv_norm
|
||||
let W = wgt_n |> Array.sum
|
||||
|
||||
// Compute mean function value on vertex P_0
|
||||
let u_0 = (Array.map2 (*) wgt_n u_n |> Array.sum) / W
|
||||
let v_0 = (Array.map2 (*) wgt_n v_n |> Array.sum) / W
|
||||
u_0, v_0
|
||||
|
||||
let centroidToVertexValues2D (grid: ExtendedGrid) (readUV: ElemIdx -> float * float) =
|
||||
let N = ((grid :> IGrid).getVertices ()).Length
|
||||
[| 0 .. (N - 1) |]
|
||||
|> Array.Parallel.map (calcVertexValue2D grid readUV)
|
||||
|
||||
|
||||
|
||||
// Given 3D centroid evaluator "readUVW", compute field value in single vertex
|
||||
// by a weighted average of values in all surrounding elements (within sigma layer)
|
||||
let calcVertexValue3D (grid: ExtendedGrid) (readUVW: ElemIdx -> float * float * float) (idx: NodeIdx) =
|
||||
// Fetch surrounding elements
|
||||
let e_n = grid.getElemsSurroundingNode idx
|
||||
|
||||
// Fetch points
|
||||
let p_0 = getVertex grid idx
|
||||
let p_n = e_n |> Array.map (getCentroid grid)
|
||||
|
||||
// Fetch function values
|
||||
let f_n = e_n |> Array.map readUVW
|
||||
let u_n = f_n |> Array.map fst3
|
||||
let v_n = f_n |> Array.map snd3
|
||||
let w_n = f_n |> Array.map trd3
|
||||
|
||||
// Compute inverse distance weights
|
||||
let wgt_n =
|
||||
p_n
|
||||
|> Array.map (fun p -> p <-> p_0)
|
||||
|> Array.map inv_norm
|
||||
let W = wgt_n |> Array.sum
|
||||
|
||||
// Compute mean function value on vertex P_0
|
||||
let u_0 = (Array.map2 (*) wgt_n u_n |> Array.sum) / W
|
||||
let v_0 = (Array.map2 (*) wgt_n v_n |> Array.sum) / W
|
||||
let w_0 = (Array.map2 (*) wgt_n w_n |> Array.sum) / W
|
||||
u_0, v_0, w_0
|
||||
|
||||
let centroidToVertexValues3D (grid: ExtendedGrid) (readUVW: ElemIdx -> float * float * float) =
|
||||
let N = ((grid :> IGrid).getVertices ()).Length
|
||||
[| 0 .. (N - 1) |]
|
||||
|> Array.Parallel.map (calcVertexValue3D grid readUVW)
|
||||
636
src/Fvcom.fs
636
src/Fvcom.fs
@@ -2,308 +2,474 @@ module Oceanbox.FvcomKit.Fvcom
|
||||
|
||||
#nowarn "57"
|
||||
|
||||
open System
|
||||
open Microsoft.Research.Science.Data
|
||||
open FSharpPlus
|
||||
open ProjNet.FSharp
|
||||
open Serilog
|
||||
open Grid
|
||||
|
||||
type FvcomGrid =
|
||||
{
|
||||
Elem: Elem array
|
||||
Nodes: Node array
|
||||
BBox: BBox
|
||||
Cells: Node array
|
||||
Bathymetry: single []
|
||||
Siglay: single [,]
|
||||
SiglayCenter: single [,]
|
||||
Siglev: single [,]
|
||||
Proj: Projection
|
||||
}
|
||||
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 x.getVertex n = x.Nodes[n]
|
||||
member x.getCell n = x.Elem[n]
|
||||
member x.getCellVertices n =
|
||||
let a, b, c = x.Elem[n]
|
||||
x.Nodes[a], x.Nodes[b], x.Nodes[c]
|
||||
member x.getVertices() = x.Nodes
|
||||
member x.getCells() = x.Elem
|
||||
member x.getBoundingBox() = x.BBox
|
||||
member x.Projection = x.Proj
|
||||
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
|
||||
Proj = WebMercator
|
||||
}
|
||||
member this.ToGrid() =
|
||||
{
|
||||
Elem = this.Elem
|
||||
Nodes = this.Nodes
|
||||
BBox = this.BBox
|
||||
Proj = this.Proj
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
member this.ToGrid() = { Elem = this.Elem; Nodes = this.Nodes; BBox = this.BBox }
|
||||
|
||||
let getNumFrames (ds: DataSet) =
|
||||
try
|
||||
ds.Dimensions["time"].Length
|
||||
with
|
||||
| e ->
|
||||
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 [,]
|
||||
let ts = ds["Times"].GetData () :?> byte[,]
|
||||
|
||||
ts[n, *]
|
||||
|> Array.map char
|
||||
|> System.String
|
||||
|> System.DateTime.Parse
|
||||
|> Some
|
||||
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 = 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 ->
|
||||
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 [,,]
|
||||
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 ->
|
||||
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 [,,]
|
||||
let ww = ds["ww"].GetData ([| t; l; 0 |], [| 1; 1; n |]) :?> single[,,]
|
||||
ww[0, 0, *]
|
||||
with
|
||||
| e ->
|
||||
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 [,,]
|
||||
let ww = ds["ww"].GetData ([| t; l; 0 |], [| 1; 1; n |]) :?> single[,,]
|
||||
es |> Array.map (fun i -> ww[0, 0, i])
|
||||
with
|
||||
| e ->
|
||||
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 [,,]
|
||||
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 ->
|
||||
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 = 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 ->
|
||||
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 [,,]
|
||||
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 ->
|
||||
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 [,]
|
||||
let zeta = ds["zeta"].GetData ([| t; 0 |], [| 1; n |]) :?> single[,]
|
||||
zeta[0, *]
|
||||
with
|
||||
| e ->
|
||||
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
|
||||
ds[ "h_center" ].GetData() :?> single []
|
||||
with
|
||||
| e ->
|
||||
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
|
||||
@@ -311,12 +477,11 @@ module Siglay =
|
||||
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 [,,]
|
||||
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 ->
|
||||
with err ->
|
||||
Log.Warning $"readUv {e} {t}"
|
||||
Log.Error $"{err}"
|
||||
Array.empty
|
||||
@@ -324,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
|
||||
@@ -337,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
|
||||
@@ -348,27 +510,59 @@ 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 =
|
||||
float (minX + (maxX - minX)) / 2., float (minY + (maxY - minY)) / 2.
|
||||
let center = float (minX + maxX) / 2., float (minY + maxY) / 2.
|
||||
{
|
||||
minX = float minX
|
||||
maxX = float maxX
|
||||
@@ -376,41 +570,40 @@ let getBBox (ds: DataSet) : BBox =
|
||||
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 []
|
||||
|> 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 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)
|
||||
let nds = Array.zip x y
|
||||
let cells = Array.zip xc yc
|
||||
let proj =
|
||||
try
|
||||
ds.Metadata["CoordinateProjection"] :?> string
|
||||
with
|
||||
| _ -> ""
|
||||
{
|
||||
Elem = elem
|
||||
Nodes = nds
|
||||
@@ -420,26 +613,65 @@ let getGrid (ds: DataSet) : FvcomGrid =
|
||||
Siglay = siglay
|
||||
SiglayCenter = siglay_c
|
||||
Siglev = siglev
|
||||
Proj = Projection.FromString proj
|
||||
}
|
||||
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 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
|
||||
}
|
||||
}
|
||||
122
src/Gradient.fs
122
src/Gradient.fs
@@ -1,122 +0,0 @@
|
||||
module Oceanbox.FvcomKit.Gradient
|
||||
|
||||
open Grid
|
||||
|
||||
let private getCell (grid: ExtendedGrid) (idx: ElemIdx) = (grid :> IGrid).getCell idx
|
||||
let private getVertex (grid: ExtendedGrid) (idx: NodeIdx) = (grid :> IGrid).getVertex idx
|
||||
let private getCentroid (grid: ExtendedGrid) (idx: ElemIdx) = (grid.getCentroids ())[idx]
|
||||
|
||||
// Compute contributions to Q and s from a single neighboring point, given current estimate of gradient df
|
||||
let private calcContributionQandS (df: float * float) (p0: Pos) (f0: float) (p1: Pos) (f1: float) =
|
||||
let x0, y0 = p0
|
||||
let x1, y1 = p1
|
||||
|
||||
let ex = x1 - x0
|
||||
let ey = y1 - y0
|
||||
|
||||
let L = sqrt (ex * ex + ey * ey)
|
||||
let L3 = L * L * L
|
||||
|
||||
let dfx, dfy = df
|
||||
let df2 = -ex * dfx - ey * dfy
|
||||
|
||||
let q1 = 4.0 * ex * ex / L3
|
||||
let q2 = 4.0 * ex * ey / L3
|
||||
let q3 = 4.0 * ey * ey / L3
|
||||
|
||||
let s1 = (6.0 * (f0 - f1) - 2.0 * df2) * ex / L3
|
||||
let s2 = (6.0 * (f0 - f1) - 2.0 * df2) * ey / L3
|
||||
|
||||
let Q = [| q1; q2; q2; q3 |]
|
||||
let s = [| s1; s2 |]
|
||||
Q, s
|
||||
|
||||
// Set up linear system to solve (Qx = s), accumulated from N neighboring points
|
||||
let private setupQandS (p0: Pos) (f0: float) (p_n: Pos []) (f_n: float []) (df: float * float) =
|
||||
let calcQS_n = calcContributionQandS df p0 f0
|
||||
|
||||
let (<+>) a b = Array.map2 (+) a b
|
||||
let rec accumulateQandS Q s idx =
|
||||
match idx with
|
||||
| [] -> Q, s
|
||||
| x :: xs ->
|
||||
let Q', s' = calcQS_n p_n[x] f_n[x]
|
||||
accumulateQandS (Q <+> Q') (s <+> s') xs
|
||||
|
||||
let Q0 = [| 0.0; 0.0; 0.0; 0.0 |]
|
||||
let s0 = [| 0.0; 0.0 |]
|
||||
let idx = [ 0 .. (p_n.Length - 1) ]
|
||||
accumulateQandS Q0 s0 idx
|
||||
|
||||
// Estimates gradient in point P_0 based on neighboring function values
|
||||
// p0: Point of interest
|
||||
// f0: Function value in POI
|
||||
// p_n: Neighboring points
|
||||
// f_n: Function values in neighboring points
|
||||
// thrs: Convergence threshold in iterative procedure
|
||||
//
|
||||
// Ref: Based on SciPy implementation which uses the following
|
||||
// G. Nielson "A method for interpolating scattered data based upon a minimum norm network" (1983)
|
||||
// R. J. Renka and A. K. Cline "A Triangle-based C1 interpolation method" (1984)
|
||||
let calcGradientFromNeighbors (p0: Pos) (f0: float) (p_n: Pos []) (f_n: float []) (thrs: float) : float * float =
|
||||
let setupQS = setupQandS p0 f0 p_n f_n
|
||||
|
||||
let rec convergeGradient (error: float) (df: float * float) =
|
||||
if error < thrs then
|
||||
df
|
||||
else
|
||||
let Q, s = setupQS df
|
||||
let detQ = Q[0] * Q[3] - Q[1] * Q[2]
|
||||
|
||||
if (abs detQ) < thrs * thrs then
|
||||
df
|
||||
else
|
||||
let r0 = (Q[3] * s[0] - Q[1] * s[1]) / detQ
|
||||
let r1 = (-Q[2] * s[0] + Q[0] * s[1]) / detQ
|
||||
let dfx, dfy = df
|
||||
let change = max (abs (dfx + r0)) (abs (dfy + r1))
|
||||
convergeGradient change (-r0, -r1)
|
||||
|
||||
let err0 = 1.0
|
||||
let grad0 = 0.0, 0.0
|
||||
convergeGradient err0 grad0
|
||||
|
||||
// Given 1D centroid evaluator "evalCentroid", compute gradient in single vertex
|
||||
let calcVertexGradient
|
||||
(grid: ExtendedGrid)
|
||||
(evalCentroid: int -> float)
|
||||
(thrs: float)
|
||||
(idx: NodeIdx, f0: float)
|
||||
: float * float =
|
||||
let neighbors =
|
||||
grid.getElemsSurroundingNode idx
|
||||
|> Array.filter (fun i -> i <> idx)
|
||||
|
||||
// Get data from vertex point of interest
|
||||
let p0 = getVertex grid idx
|
||||
|
||||
// Get data from neighbor elements of POI
|
||||
let p_n = neighbors |> Array.map (getCentroid grid)
|
||||
let f_n = neighbors |> Array.map evalCentroid
|
||||
|
||||
calcGradientFromNeighbors p0 f0 p_n f_n thrs
|
||||
|
||||
// Given 1D vertex evaluator "evalVertex", compute gradient in single vertex
|
||||
let calcVertexGradient'
|
||||
(grid: ExtendedGrid)
|
||||
(evalVertex: int -> float)
|
||||
(thrs: float)
|
||||
(idx: NodeIdx, f0: float)
|
||||
: float * float =
|
||||
let neighbors =
|
||||
grid.getNodesSurroundingNode idx
|
||||
|> Array.filter (fun i -> i <> idx)
|
||||
|
||||
// Get data from vertex point of interest
|
||||
let p0 = getVertex grid idx
|
||||
|
||||
// Get data from neighbor elements of POI
|
||||
let p_n = neighbors |> Array.map (getVertex grid)
|
||||
let f_n = neighbors |> Array.map evalVertex
|
||||
|
||||
calcGradientFromNeighbors p0 f0 p_n f_n thrs
|
||||
574
src/Grid.fs
574
src/Grid.fs
@@ -6,9 +6,12 @@ open FSharpPlus.Control
|
||||
open ProjNet.CoordinateSystems
|
||||
open Serilog
|
||||
open ProjNet.FSharp
|
||||
open MessagePack
|
||||
open MBrace.FsPickler
|
||||
open FsKDTree
|
||||
open KdTree // C# version
|
||||
//open FsKDTree
|
||||
open KdTree // NOTE: C# version
|
||||
|
||||
open Types
|
||||
|
||||
type NodeIdx = int
|
||||
type ElemIdx = int
|
||||
@@ -18,119 +21,75 @@ type Elem = NodeIdx * NodeIdx * NodeIdx
|
||||
type Node = float * float
|
||||
|
||||
type Pos = float * float
|
||||
type Field = (float * float) []
|
||||
|
||||
type Pos3D = float * float * float
|
||||
|
||||
type Field4D =
|
||||
{
|
||||
Frame0: int
|
||||
Frame1: int
|
||||
Time0: float
|
||||
Time1: float
|
||||
Field0: (float * float * float) [] []
|
||||
Field1: (float * float * float) [] []
|
||||
Siglays: float []
|
||||
Bath: float []
|
||||
}
|
||||
|
||||
type BBox =
|
||||
{
|
||||
minX: float
|
||||
maxX: float
|
||||
minY: float
|
||||
maxY: float
|
||||
center: float * float
|
||||
}
|
||||
static member empty =
|
||||
{
|
||||
minX = Double.MaxValue
|
||||
maxX = Double.MinValue
|
||||
minY = Double.MaxValue
|
||||
maxY = Double.MinValue
|
||||
center = 0, 0
|
||||
}
|
||||
type Leaf<'a> = { Pos: Pos; Data: 'a }
|
||||
type Field = (float * float) array
|
||||
|
||||
type Cell = NodeIdx * NodeIdx * NodeIdx
|
||||
type Vertex = float * float
|
||||
|
||||
type IGrid =
|
||||
abstract getVertex: int -> Vertex
|
||||
abstract getCell: int -> Cell
|
||||
abstract getCellVertices: int -> Vertex * Vertex * Vertex
|
||||
abstract getVertices: unit -> Vertex []
|
||||
abstract getCells: unit -> Cell []
|
||||
abstract getVertices: unit -> Vertex array
|
||||
abstract getCells: unit -> Cell array
|
||||
abstract getBoundingBox: unit -> BBox
|
||||
abstract Projection: Projection
|
||||
|
||||
type Grid =
|
||||
{
|
||||
Elem: Elem array
|
||||
Nodes: Node array
|
||||
BBox: BBox
|
||||
Proj: Projection
|
||||
}
|
||||
type Grid = {
|
||||
Elem: Elem array
|
||||
Nodes: Node array
|
||||
BBox: BBox
|
||||
} with
|
||||
interface IGrid with
|
||||
member x.getVertex n = x.Nodes[n]
|
||||
member x.getCell n = x.Elem[n]
|
||||
member x.getCellVertices n =
|
||||
let a, b, c = x.Elem[n]
|
||||
x.Nodes[a], x.Nodes[b], x.Nodes[c]
|
||||
member x.getVertices() = x.Nodes
|
||||
member x.getCells() = x.Elem
|
||||
member x.getBoundingBox() = x.BBox
|
||||
member x.Projection = x.Proj
|
||||
static member empty =
|
||||
{
|
||||
Elem = Array.empty
|
||||
Nodes = Array.empty
|
||||
BBox = BBox.empty
|
||||
Proj = WebMercator
|
||||
}
|
||||
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 ElemsAroundNode = Map<NodeIdx, ElemIdx []>
|
||||
type NodesAroundNode = Map<NodeIdx, NodeIdx []>
|
||||
type ElemsAroundNode = Map<NodeIdx, ElemIdx array>
|
||||
type NodesAroundNode = Map<NodeIdx, NodeIdx array>
|
||||
type ElemsAroundElem = Map<ElemIdx, ElemIdx array>
|
||||
|
||||
type NeighborIndex =
|
||||
{
|
||||
ElemsAroundNode: ElemsAroundNode
|
||||
NodesAroundNode: NodesAroundNode
|
||||
}
|
||||
static member empty = { ElemsAroundNode = Map.empty; NodesAroundNode = Map.empty }
|
||||
|
||||
type private Ean = Map<NodeIdx, ElemIdx list>
|
||||
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 treeLeafSize = LeafNodeSize 64
|
||||
|
||||
let private createTree (points: Leaf<double, int> []) =
|
||||
let tree = KdTree<float, int>(2, KdTree.Math.DoubleMath())
|
||||
points
|
||||
|> Array.iter (fun a -> tree.Add([| a.Pos.X; a.Pos.Y |], a.Data) |> ignore)
|
||||
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
|
||||
tree.Balance()
|
||||
do tree.Balance ()
|
||||
else
|
||||
Log.Warning $"Empty kd-tree"
|
||||
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 =
|
||||
@@ -141,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
|
||||
@@ -157,28 +113,45 @@ 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 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
|
||||
|
||||
{
|
||||
ElemsAroundNode = ean
|
||||
NodesAroundNode = makeNodesSurroudingNodeMap ean elem
|
||||
ElemsAroundElem = makeElemsSurroundingElemMap ean elem
|
||||
}
|
||||
|
||||
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]
|
||||
@@ -200,7 +173,7 @@ let calcBBox nodes =
|
||||
let printBBox grid = calcBBox grid.Nodes |> printfn "%A"
|
||||
|
||||
let bboxToLngLat (coordsys: CoordinateSystem) b =
|
||||
let toLatLon = makeTransform coordsys Transformations.WGS84
|
||||
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)
|
||||
@@ -215,45 +188,68 @@ let bboxToLngLat (coordsys: CoordinateSystem) 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 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 Transformations.EPSG3857
|
||||
let s = System.Diagnostics.Stopwatch.StartNew()
|
||||
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
|
||||
Some (nodes, nele)
|
||||
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
|
||||
|
||||
@@ -276,9 +272,9 @@ let private readNodes (f: string array) =
|
||||
let private readObc (f: string array) =
|
||||
let p =
|
||||
function
|
||||
| [| _; x; _ |] -> int x
|
||||
| [| x; _ |] -> int x
|
||||
| [| x |] -> int x
|
||||
| [| _; x; _ |] -> int x - 1
|
||||
| [| x; _ |] -> int x - 1
|
||||
| [| x |] -> int x - 1
|
||||
| _ -> failwith "readObc failed"
|
||||
reader p f
|
||||
|
||||
@@ -290,15 +286,14 @@ let readGrdFile (filename: string) =
|
||||
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 }
|
||||
toGrid <!> elem <*> nodes)
|
||||
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)
|
||||
|
||||
module Boundary =
|
||||
let normalizeElement (a, b, c) =
|
||||
@@ -312,11 +307,8 @@ module Boundary =
|
||||
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])
|
||||
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
|
||||
@@ -334,8 +326,7 @@ module Boundary =
|
||||
|> 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
|
||||
edgeMap |> Map.fold (fun a k v -> Map.add v k a) Map.empty
|
||||
|
||||
let getBoundaryNodesArray (edgeMap: Map<Edge, ElemIdx>) =
|
||||
edgeMap
|
||||
@@ -355,8 +346,7 @@ module Util =
|
||||
(x0 + x1 + x2) / 3f, (y0 + y1 + y2) / 3f
|
||||
|
||||
static member calcArea((x0, y0), (x1, y1), (x2, y2)) =
|
||||
x0 * (y1 - y2) + x1 * (y2 - y0) + x2 * (y0 - y1)
|
||||
|> (*) 0.5
|
||||
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)
|
||||
@@ -374,30 +364,47 @@ module Util =
|
||||
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"
|
||||
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 |]
|
||||
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))
|
||||
ns |> Array.fold (fun a n -> s[n] + a) 0. |> (*) (1. / float ns.Length)
|
||||
)
|
||||
|
||||
static member speedToNodal (nIdx: NeighborIndex) (s: (float * float) []) =
|
||||
[| 0 .. nIdx.NodesAroundNode.Count - 1 |]
|
||||
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)
|
||||
sqrt (u * u + v * v) + a
|
||||
)
|
||||
0.
|
||||
|> (*) (1. / float ns.Length))
|
||||
|> (*) (1. / float ns.Length)
|
||||
)
|
||||
|
||||
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) =
|
||||
@@ -416,8 +423,10 @@ module Util =
|
||||
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)
|
||||
a1 + a2
|
||||
)
|
||||
|> Array.sum
|
||||
)
|
||||
|
||||
static member calcNodeArea (idx: NeighborIndex) (grid: IGrid) =
|
||||
let nodes = grid.getVertices ()
|
||||
@@ -425,7 +434,8 @@ module Util =
|
||||
|> Array.Parallel.map (fun n ->
|
||||
getElemsSurroundingNode idx n
|
||||
|> Array.map (grid.getCellVertices >> Element.calcArea)
|
||||
|> Array.sum)
|
||||
|> Array.sum
|
||||
)
|
||||
|
||||
let calcCentroids (grid: IGrid) =
|
||||
let n = grid.getVertices ()
|
||||
@@ -434,42 +444,29 @@ module Util =
|
||||
let p0 = n[a]
|
||||
let p1 = n[b]
|
||||
let p2 = n[c]
|
||||
Element.calcCentroid (p0, p1, p2))
|
||||
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)
|
||||
(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.)
|
||||
let neg = (d1 < 0.) || (d2 < 0.) || (d3 < 0.)
|
||||
let pos = (d1 > 0.) || (d2 > 0.) || (d3 > 0.)
|
||||
|
||||
neg && pos |> not
|
||||
|
||||
let inline isInsideTriangle_dbl (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.0) || (d2 <= 0.0) || (d3 <= 0.0)
|
||||
let pos = (d1 >= 0.0) || (d2 >= 0.0) || (d3 >= 0.0)
|
||||
|
||||
neg && pos |> not
|
||||
(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 = x; Y = y }; Data = i })
|
||||
{ Pos = x, y; Data = i }
|
||||
)
|
||||
// |> create2DTree treeLeafSize
|
||||
|> createTree
|
||||
|
||||
@@ -477,12 +474,10 @@ module Util =
|
||||
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 })
|
||||
let pos = i |> grid.getCellVertices |> Element.calcCentroid
|
||||
// |> fun (x, y) -> { X = x; Y = y }
|
||||
{ Pos = pos; Data = i }
|
||||
)
|
||||
// |> create2DTree treeLeafSize
|
||||
|> createTree
|
||||
|
||||
@@ -498,25 +493,38 @@ module Util =
|
||||
// else None)
|
||||
|
||||
let tryFindElement (grid: IGrid) (tree: KdTree<float, int>) ((p0, p1): float * float) =
|
||||
tree.GetNearestNeighbours([| p0; p1 |], 1)
|
||||
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)
|
||||
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 centroids: Vertex[] option = None
|
||||
let mutable gridHash: byte[] = [||]
|
||||
|
||||
let getNeighborIdx () =
|
||||
match neighborIndex with
|
||||
@@ -526,97 +534,117 @@ type ExtendedGrid(grid: IGrid) =
|
||||
neighborIndex.Value
|
||||
|
||||
interface IGrid with
|
||||
member x.getVertex n = grid.getVertex n
|
||||
member x.getCell n = grid.getCell n
|
||||
member x.getCellVertices n = grid.getCellVertices n
|
||||
member x.getVertices() = grid.getVertices ()
|
||||
member x.getCells() = grid.getCells ()
|
||||
member x.getBoundingBox() = grid.getBoundingBox ()
|
||||
member x.Projection = grid.Projection
|
||||
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 x.NeighborIndex = neighborIndex
|
||||
member x.NodeTree = nodeTree
|
||||
member x.ElementTree = elementTree
|
||||
member this.NeighborIndex = neighborIndex
|
||||
member this.NodeTree = nodeTree
|
||||
member this.ElementTree = elementTree
|
||||
|
||||
member x.Grid = grid
|
||||
member this.Grid = grid
|
||||
|
||||
member x.initNeighborIndex(?cache: string) =
|
||||
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 x.initNodeTree() =
|
||||
member this.initNodeTree() =
|
||||
match nodeTree with
|
||||
| Some _ -> ()
|
||||
| None -> nodeTree <- Util.buildNearestNodeTree grid |> Some
|
||||
|
||||
member x.initElementTree() =
|
||||
member this.initElementTree() =
|
||||
match elementTree with
|
||||
| Some _ -> ()
|
||||
| None -> elementTree <- Util.buildNearestElementTree grid |> Some
|
||||
|
||||
// member x.nearestNode(p0: single, p1: single) =
|
||||
// let nearest (tree: Tree<_, _>) =
|
||||
// nearestNeighbor tree { X = p0; Y = p1 }
|
||||
// |> Option.map (fun l -> l.Data)
|
||||
// match nodeTree with
|
||||
// | Some tree -> nearest tree
|
||||
// | None ->
|
||||
// x.initNodeTree ()
|
||||
// nearest nodeTree.Value
|
||||
|
||||
member x.nearestNode(p0: float, p1: float) =
|
||||
member this.nearestNode(p0: float, p1: float) =
|
||||
let nearest (tree: KdTree<_, _>) =
|
||||
tree.GetNearestNeighbours([| p0; p1 |], 1)
|
||||
tree.GetNearestNeighbours ([| p0; p1 |], 1)
|
||||
|> Array.tryHead
|
||||
|> Option.map (fun l -> l.Value)
|
||||
match nodeTree with
|
||||
| Some tree -> nearest tree
|
||||
| None ->
|
||||
x.initNodeTree ()
|
||||
this.initNodeTree ()
|
||||
nearest nodeTree.Value
|
||||
|
||||
// member private x.tryFindElementTwice (grid: IGrid) (tree: IdxTree) ((p0, p1): single * single as p) =
|
||||
// match Util.tryFindElement grid tree p with
|
||||
// | Some elemIdx -> Some elemIdx
|
||||
// | None ->
|
||||
// nearestNeighbor tree { X = p0; Y = p1 }
|
||||
// |> Option.bind (fun leaf ->
|
||||
// x.getElemsSurroundingElem leaf.Data
|
||||
// |> Array.tryFind (fun eIdx ->
|
||||
// let vx = x.Grid.getCellVertices eIdx
|
||||
// Util.isInsideTriangle vx p))
|
||||
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 x.tryFindElementTwice (grid: IGrid) (tree: IdxTree) ((p0, p1): float * float as p) =
|
||||
match Util.tryFindElement grid tree p with
|
||||
| Some elemIdx -> Some elemIdx
|
||||
| None ->
|
||||
tree.GetNearestNeighbours([| p0; p1 |], 1)
|
||||
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 ->
|
||||
x.getElemsSurroundingElem leaf.Value
|
||||
this.getElemsSurroundingElem leaf.Value
|
||||
|> Array.tryFind (fun eIdx ->
|
||||
let vx = x.Grid.getCellVertices eIdx
|
||||
Util.isInsideTriangle vx p))
|
||||
let vx = this.Grid.getCellVertices eIdx
|
||||
Util.isInsideTriangle vx p
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
member x.tryGetElement p =
|
||||
match elementTree with
|
||||
| Some tree -> x.tryFindElementTwice grid tree p
|
||||
| None ->
|
||||
x.initElementTree ()
|
||||
Log.Debug $"You shouldn't be here!"
|
||||
// x.initNodeTree ()
|
||||
x.tryFindElementTwice grid elementTree.Value 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 x.tryGetElementSloppy(p0, p1 as p) =
|
||||
member this.tryGetElementSloppy(p0, p1 as p) =
|
||||
match elementTree with
|
||||
| Some tree -> Util.tryFindElement grid tree p
|
||||
| None ->
|
||||
x.initElementTree ()
|
||||
// x.initNodeTree ()
|
||||
this.initElementTree ()
|
||||
// this.initNodeTree ()
|
||||
Util.tryFindElement grid elementTree.Value p
|
||||
|
||||
member x.getCentroids() =
|
||||
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 ->
|
||||
@@ -624,58 +652,55 @@ type ExtendedGrid(grid: IGrid) =
|
||||
centroids <- Some cx
|
||||
cx
|
||||
|
||||
member x.calcCircumCircle e =
|
||||
member this.calcCircumCircle e =
|
||||
let triangle = grid.getCellVertices e
|
||||
Util.Element.calcCircumscribedCircle triangle
|
||||
|
||||
member x.getElemsSurroundingNode n =
|
||||
getNeighborIdx ()
|
||||
|> fun idx -> idx.ElemsAroundNode[n]
|
||||
member this.getElemsSurroundingNode n =
|
||||
getNeighborIdx () |> fun idx -> idx.ElemsAroundNode[n]
|
||||
|
||||
member x.getNodesSurroundingNode n =
|
||||
getNeighborIdx ()
|
||||
|> fun idx -> idx.NodesAroundNode[n]
|
||||
member this.getNodesSurroundingNode n =
|
||||
getNeighborIdx () |> fun idx -> idx.NodesAroundNode[n]
|
||||
|
||||
member x.getNodesSurroundingElem e =
|
||||
member this.getNodesSurroundingElem e =
|
||||
let idx = getNeighborIdx ()
|
||||
let elem = grid.getCell e
|
||||
getSurrounding idx.NodesAroundNode elem
|
||||
|
||||
member x.getElemsSurroundingElem e =
|
||||
member this.getElemsSurroundingElem e =
|
||||
let idx = getNeighborIdx ()
|
||||
let elem = grid.getCell e
|
||||
getSurrounding idx.ElemsAroundNode elem
|
||||
|
||||
member x.saveNeighborIndex(fname: string) =
|
||||
let binarySerializer = FsPickler.CreateBinarySerializer()
|
||||
member this.saveNeighborIndex(fname: string) =
|
||||
let binarySerializer = FsPickler.CreateBinarySerializer ()
|
||||
let nix = getNeighborIdx ()
|
||||
let pickle = binarySerializer.Pickle nix
|
||||
IO.File.WriteAllBytes(fname, pickle)
|
||||
IO.File.WriteAllBytes (fname, pickle)
|
||||
|
||||
member x.loadNeighborIndex(fname: string) =
|
||||
let binarySerializer = FsPickler.CreateBinarySerializer()
|
||||
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
|
||||
neighborIndex <- binarySerializer.UnPickle<NeighborIndex> pickle |> Some
|
||||
true
|
||||
else
|
||||
false
|
||||
|
||||
member x.saveNodeTree(fname: string) =
|
||||
let binarySerializer = FsPickler.CreateBinarySerializer()
|
||||
member this.saveNodeTree(fname: string) =
|
||||
let binarySerializer = FsPickler.CreateBinarySerializer ()
|
||||
let tree =
|
||||
nodeTree
|
||||
|> Option.defaultWith (fun () ->
|
||||
x.initNodeTree ()
|
||||
nodeTree.Value)
|
||||
this.initNodeTree ()
|
||||
nodeTree.Value
|
||||
)
|
||||
|
||||
let pickle = binarySerializer.Pickle tree
|
||||
IO.File.WriteAllBytes(fname, pickle)
|
||||
IO.File.WriteAllBytes (fname, pickle)
|
||||
|
||||
member x.loadNodeTree(fname: string) =
|
||||
let binarySerializer = FsPickler.CreateBinarySerializer()
|
||||
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
|
||||
@@ -683,19 +708,20 @@ type ExtendedGrid(grid: IGrid) =
|
||||
else
|
||||
false
|
||||
|
||||
member x.saveElementTree(fname: string) =
|
||||
let binarySerializer = FsPickler.CreateBinarySerializer()
|
||||
member this.saveElementTree(fname: string) =
|
||||
let binarySerializer = FsPickler.CreateBinarySerializer ()
|
||||
let tree =
|
||||
elementTree
|
||||
|> Option.defaultWith (fun () ->
|
||||
x.initElementTree ()
|
||||
elementTree.Value)
|
||||
this.initElementTree ()
|
||||
elementTree.Value
|
||||
)
|
||||
|
||||
let pickle = binarySerializer.Pickle tree
|
||||
IO.File.WriteAllBytes(fname, pickle)
|
||||
IO.File.WriteAllBytes (fname, pickle)
|
||||
|
||||
member x.loadElementTree(fname: string) =
|
||||
let binarySerializer = FsPickler.CreateBinarySerializer()
|
||||
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
|
||||
|
||||
@@ -8,8 +8,8 @@ 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
|
||||
@@ -21,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)
|
||||
@@ -40,42 +41,36 @@ 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
|
||||
|
||||
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
|
||||
|
||||
type BiWght = float * float * float * float
|
||||
type BiProp = float * float * float * float
|
||||
@@ -92,47 +87,43 @@ let genBilinearInterpolationWgts
|
||||
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
|
||||
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
|
||||
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)
|
||||
Some (w00, w10, w11, w01)
|
||||
else
|
||||
None
|
||||
|
||||
let makeBiWeights
|
||||
(pos: (float * float) [])
|
||||
(boxes: ((float * float) * (float * float) * (float * float) * (float * float)) [])
|
||||
(mask: (bool * bool * bool * bool) [])
|
||||
(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 []) =
|
||||
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)
|
||||
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 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 private fillOutOfBounds (grid: Grid) (interpVal: float option[]) =
|
||||
let node = interpVal.Length = grid.Nodes.Length
|
||||
let nbridx = makeNeighborIndex (grid :> IGrid)
|
||||
let mutable allval = valtest interpVal
|
||||
@@ -141,24 +132,20 @@ let private fillOutOfBounds (grid: Grid) (interpVal: float option []) =
|
||||
if Option.isNone interpVal[n] then
|
||||
let nb =
|
||||
if node then
|
||||
nbridx.NodesAroundNode[n]
|
||||
|> Array.filter (fun k -> Option.isSome interpVal[k])
|
||||
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
|
||||
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 [,])
|
||||
(pos: (float * float)[])
|
||||
(prop: float[,])
|
||||
(fvgrid: Grid)
|
||||
(oob: bool)
|
||||
=
|
||||
@@ -189,7 +176,7 @@ let interpolateProp
|
||||
else
|
||||
iprop |> Array.map (fun v -> v.Value)
|
||||
|
||||
let interpCoefs (coords, _ as grid: BiPos * Mask) (pos: (float * float) []) =
|
||||
let interpCoefs (coords, _ as grid: BiPos * Mask) (pos: (float * float)[]) =
|
||||
let cm = getNearestCellCorner grid pos
|
||||
let boxes, mask =
|
||||
cm
|
||||
@@ -198,12 +185,10 @@ let interpCoefs (coords, _ as grid: BiPos * Mask) (pos: (float * float) []) =
|
||||
|> Array.unzip
|
||||
let wgths = makeBiWeights pos boxes mask
|
||||
let corneridx =
|
||||
cm
|
||||
|> Array.map (fun c -> Array.unzip c)
|
||||
|> Array.map (fun (i, _) -> i)
|
||||
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 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]))
|
||||
|
||||
@@ -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))
|
||||
@@ -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,8 +41,7 @@ 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)
|
||||
|
||||
|
||||
@@ -2,17 +2,19 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<IsPackable>true</IsPackable>
|
||||
<PackageId>Oceanbox.FvcomKit</PackageId>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Company>Oceanbox AS</Company>
|
||||
<Authors/>
|
||||
<Company/>
|
||||
<Version>4.4.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="Polygon.fs"/>
|
||||
@@ -22,27 +24,23 @@
|
||||
<Compile Include="NorKyst.fs"/>
|
||||
<Compile Include="NorShelf.fs"/>
|
||||
<Compile Include="Smoothing.fs"/>
|
||||
<Compile Include="Gradient.fs"/>
|
||||
<Compile Include="CloughTocher.fs"/>
|
||||
<Compile Include="EvaluateVertex.fs"/>
|
||||
<Compile Include="Evaluate2D.fs"/>
|
||||
<Compile Include="Evaluate4D.fs"/>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Update="FSharp.Core" Version="6.0.5"/>
|
||||
<PackageReference Include="FSharp.Data" Version="6.0.1-beta001"/>
|
||||
<PackageReference Include="FSharpPlus" Version="1.3.0-CI02744"/>
|
||||
<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="sdslite" Version="2.5.0"/>
|
||||
<PackageReference Include="Serilog" Version="2.12.0"/>
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.1-dev-00896"/>
|
||||
<PackageReference Include="Serilog.Sinks.Seq" Version="5.2.1"/>
|
||||
<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>
|
||||
<ProjectReference Include="..\submodules\ProjNet.FSharp\src\ProjNet.FSharp.fsproj"/>
|
||||
<ProjectReference Include="..\submodules\FsKdTree\lib\FsKDTree.fsproj"/>
|
||||
<PackageUpdate Include="ProjNet.FSharp" Version="*" Condition=" '$(ContinuousIntegrationBuild)'=='true' "/>
|
||||
<PackageUpdate Include="Oceanbox.SDSLite" Version="*" Condition=" '$(ContinuousIntegrationBuild)'=='true' "/>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
type Point = float * float
|
||||
|
||||
type Polygon = Point []
|
||||
type Polygon = Point[]
|
||||
|
||||
let private maxdist (pol: Polygon) (pnt: Point) =
|
||||
pol
|
||||
@@ -28,10 +28,7 @@ let private onsegment (p: Polygon) =
|
||||
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
|
||||
if x[2] >= minx && x[2] <= maxx && y[2] >= miny && y[2] <= maxy then
|
||||
true
|
||||
else
|
||||
false
|
||||
@@ -56,8 +53,7 @@ let private intersect (p1: Polygon) (p2: Polygon) =
|
||||
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 (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
|
||||
|
||||
186
src/ROMS.fs
186
src/ROMS.fs
@@ -11,36 +11,33 @@ open Adjoin
|
||||
open Interpol
|
||||
|
||||
type GridData<'a> = { rho: 'a; u: 'a; v: 'a }
|
||||
type GridMask = GridData<bool [,]>
|
||||
type GridMask = GridData<bool[,]>
|
||||
type GridPos = GridData<BiPos>
|
||||
type GridVars = GridData<float [,]>
|
||||
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
|
||||
|
||||
@@ -49,58 +46,45 @@ let private layerZ csR (vars: GridVars) =
|
||||
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..."
|
||||
@@ -119,32 +103,29 @@ 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
|
||||
@@ -152,31 +133,28 @@ let readGrid (nc: DataSet) =
|
||||
|
||||
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
|
||||
@@ -201,13 +179,13 @@ let genRomsToFvcomAdjoints (proj: IProj) (fvcom: Grid) (roms: RomsGrid) =
|
||||
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 (proj: IProj) (fvcom: Grid) (roms: RomsGrid) (props: RomsProps) =
|
||||
@@ -216,8 +194,7 @@ let adjoinRomsToFvcom (proj: IProj) (fvcom: Grid) (roms: RomsGrid) (props: RomsP
|
||||
|
||||
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
|
||||
@@ -231,23 +208,18 @@ 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
|
||||
@@ -256,9 +228,7 @@ let calcUVBar (fvcom: FvcomGrid) (u, 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)
|
||||
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
|
||||
|
||||
@@ -7,61 +7,61 @@ 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
|
||||
@@ -73,34 +73,34 @@ 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
|
||||
|
||||
@@ -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
|
||||
|
||||
25
src/Types.fs
25
src/Types.fs
@@ -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
41
src/default.nix
Normal 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
272
src/deps.json
Normal 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="
|
||||
}
|
||||
]
|
||||
Submodule submodules/FsKdTree deleted from b6e14c0a81
Submodule submodules/ProjNet.FSharp deleted from 7446d10f20
@@ -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
|
||||
@@ -1,16 +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" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
189
xtest/Arome.fs
Normal file
189
xtest/Arome.fs
Normal 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
38
xtest/xtest.fsproj
Normal 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
3
xtest/xunit.runner.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"$schema": "https://xunit.net/schema/current/xunit.runner.schema.json"
|
||||
}
|
||||
Reference in New Issue
Block a user