Merge branch 'mrtz/e2e' into 'main'
feat: Add ElemsAroundElem to NeighborIndex See merge request oceanbox/Oceanbox.FvcomKit!34
This commit was merged in pull request #35.
This commit is contained in:
@@ -11,6 +11,10 @@ insert_final_newline = false
|
||||
indent_size = 2
|
||||
max_line_length= 80
|
||||
|
||||
[*.nix]
|
||||
indent_size = 2
|
||||
max_line_length= 80
|
||||
|
||||
[*.fs]
|
||||
max_line_length= 120
|
||||
|
||||
@@ -21,6 +25,7 @@ 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
|
||||
|
||||
16
shell.nix
16
shell.nix
@@ -2,15 +2,19 @@ with import <nixpkgs> { };
|
||||
let
|
||||
dotnet-sdk = dotnet-sdk_9;
|
||||
in
|
||||
mkShell {
|
||||
buildInputs = [
|
||||
netcdf
|
||||
mkShell rec {
|
||||
packages = [
|
||||
bun
|
||||
dotnet-sdk
|
||||
fsautocomplete
|
||||
fantomas
|
||||
];
|
||||
|
||||
buildInputs = [
|
||||
netcdf
|
||||
stdenv.cc.cc.lib
|
||||
];
|
||||
|
||||
DOTNET_ROOT = "${dotnet-sdk}/share/dotnet";
|
||||
shellHook = ''
|
||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:${stdenv.cc.cc.lib}/lib;
|
||||
'';
|
||||
LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath buildInputs;
|
||||
}
|
||||
|
||||
331
src/Grid.fs
331
src/Grid.fs
@@ -9,7 +9,7 @@ open ProjNet.FSharp
|
||||
open MessagePack
|
||||
open MBrace.FsPickler
|
||||
//open FsKDTree
|
||||
open KdTree // C# version
|
||||
open KdTree // NOTE: C# version
|
||||
|
||||
open Types
|
||||
|
||||
@@ -21,8 +21,8 @@ type Elem = NodeIdx * NodeIdx * NodeIdx
|
||||
type Node = float * float
|
||||
|
||||
type Pos = float * float
|
||||
type Leaf<'a> = { Pos : Pos; Data : 'a }
|
||||
type Field = (float * float) []
|
||||
type Leaf<'a> = { Pos: Pos; Data: 'a }
|
||||
type Field = (float * float) array
|
||||
|
||||
type Cell = NodeIdx * NodeIdx * NodeIdx
|
||||
|
||||
@@ -30,16 +30,15 @@ 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
|
||||
|
||||
type Grid =
|
||||
{
|
||||
Elem: Elem array
|
||||
Nodes: Node array
|
||||
BBox: BBox
|
||||
}
|
||||
type Grid = {
|
||||
Elem: Elem array
|
||||
Nodes: Node array
|
||||
BBox: BBox
|
||||
} with
|
||||
interface IGrid with
|
||||
member this.getVertex n = this.Nodes[n]
|
||||
member this.getCell n = this.Elem[n]
|
||||
@@ -49,34 +48,30 @@ type Grid =
|
||||
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
|
||||
}
|
||||
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 NeighborIndex = {
|
||||
ElemsAroundNode: ElemsAroundNode
|
||||
NodesAroundNode: NodesAroundNode
|
||||
ElemsAroundElem: ElemsAroundElem
|
||||
} with
|
||||
static member empty = { ElemsAroundNode = Map.empty; NodesAroundNode = Map.empty; ElemsAroundElem = Map.empty }
|
||||
|
||||
type private Ean = Map<NodeIdx, ElemIdx list>
|
||||
|
||||
// NOTE(SimenLK): The amount of items to be stored in the trees leafs
|
||||
// let treeLeafSize = LeafNodeSize 64
|
||||
|
||||
let private createTree (points: Leaf<int> []) =
|
||||
let tree = KdTree<float, int>(2, KdTree.Math.DoubleMath())
|
||||
let private createTree (points: Leaf<int> array) =
|
||||
let tree = KdTree<float, int> (2, KdTree.Math.DoubleMath ())
|
||||
points
|
||||
|> Array.iter (fun a -> tree.Add([| fst a.Pos; snd a.Pos |], a.Data) |> ignore)
|
||||
|> 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
|
||||
@@ -89,12 +84,9 @@ let private makeElemsSurroundingNodeMap (elem: Elem array) : ElemsAroundNode =
|
||||
elem
|
||||
|> Array.fold
|
||||
(fun (n, acc) (a, b, c) ->
|
||||
let acc' =
|
||||
acc
|
||||
|> addElIdx a n
|
||||
|> addElIdx b n
|
||||
|> addElIdx c n
|
||||
n + 1, acc')
|
||||
let acc' = acc |> addElIdx a n |> addElIdx b n |> addElIdx c n
|
||||
n + 1, acc'
|
||||
)
|
||||
(0, Map.empty)
|
||||
|> snd
|
||||
|> Map.mapValues toArray
|
||||
@@ -108,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
|
||||
@@ -124,13 +113,26 @@ 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 ()
|
||||
@@ -138,14 +140,17 @@ let makeNeighborIndex (grid: IGrid) =
|
||||
{
|
||||
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]
|
||||
@@ -182,69 +187,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 }
|
||||
{
|
||||
minX = x0
|
||||
maxX = x1
|
||||
minY = y0
|
||||
maxY = y1
|
||||
center = proj b.center
|
||||
}
|
||||
|
||||
let projectGrid proj (grid: Grid) : Grid =
|
||||
{ grid with
|
||||
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'
|
||||
}
|
||||
{ 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'
|
||||
}
|
||||
{ grid with Nodes = nodes'; BBox = bbox' }
|
||||
|
||||
let toWebMercator (coordsys: CoordinateSystem) (grid: Grid) =
|
||||
let toWebMercator = makeTransform coordsys CoordSys.EPSG3857
|
||||
let s = System.Diagnostics.Stopwatch.StartNew()
|
||||
let 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 s = System.Diagnostics.Stopwatch.StartNew ()
|
||||
let g = grid |> projectGrid toLonLat.project
|
||||
s.Stop()
|
||||
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
|
||||
|
||||
@@ -282,13 +286,13 @@ let readGrdFile (filename: string) =
|
||||
let elem = readElem els
|
||||
let nodes = readNodes nds
|
||||
let toGrid e n = { Elem = e; Nodes = n; BBox = calcBBox n }
|
||||
toGrid <!> elem <*> nodes)
|
||||
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) =
|
||||
@@ -302,11 +306,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
|
||||
@@ -324,8 +325,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
|
||||
@@ -345,8 +345,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)
|
||||
@@ -364,20 +363,20 @@ 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 []) =
|
||||
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) []) =
|
||||
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]
|
||||
@@ -385,11 +384,13 @@ module Util =
|
||||
|> 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) []) =
|
||||
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]
|
||||
@@ -398,9 +399,11 @@ module Util =
|
||||
|> Array.fold
|
||||
(fun (au, av) n ->
|
||||
let u, v = s[n]
|
||||
u + au, v + av)
|
||||
u + au, v + av
|
||||
)
|
||||
(0., 0.)
|
||||
|> fun (u, v) -> u / n, v / n)
|
||||
|> fun (u, v) -> u / n, v / n
|
||||
)
|
||||
|
||||
type Node =
|
||||
static member calcNodeControlArea (idx: NeighborIndex) (grid: IGrid) =
|
||||
@@ -419,8 +422,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 ()
|
||||
@@ -428,7 +433,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 ()
|
||||
@@ -437,12 +443,12 @@ 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
|
||||
@@ -458,7 +464,8 @@ module Util =
|
||||
grid.getVertices ()
|
||||
|> Array.mapi (fun i v ->
|
||||
let x, y = v
|
||||
{ Pos = x, y; Data = i })
|
||||
{ Pos = x, y; Data = i }
|
||||
)
|
||||
// |> create2DTree treeLeafSize
|
||||
|> createTree
|
||||
|
||||
@@ -466,12 +473,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
|
||||
|
||||
@@ -487,14 +492,15 @@ 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>>
|
||||
@@ -503,22 +509,20 @@ type private NodeIdxTree = KdTree<float, int>
|
||||
|
||||
[<MessagePackObject>]
|
||||
type BinGrid = {
|
||||
[<Key(0)>] hash : byte[]
|
||||
[<Key(1)>] vertices : (float * float)[]
|
||||
[<Key(2)>] cells : (int * int * int)[]
|
||||
[<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
|
||||
}
|
||||
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 () =
|
||||
@@ -542,12 +546,11 @@ type ExtendedGrid(grid: IGrid) =
|
||||
|
||||
member this.Grid = grid
|
||||
|
||||
member this.ToGrid() =
|
||||
{
|
||||
Nodes = this.Grid.getVertices ()
|
||||
Elem = this.Grid.getCells ()
|
||||
BBox = this.Grid.getBoundingBox ()
|
||||
}
|
||||
member this.ToGrid() = {
|
||||
Nodes = this.Grid.getVertices ()
|
||||
Elem = this.Grid.getCells ()
|
||||
BBox = this.Grid.getBoundingBox ()
|
||||
}
|
||||
|
||||
|
||||
member this.initNeighborIndex(?cache: string) =
|
||||
@@ -567,7 +570,7 @@ type ExtendedGrid(grid: IGrid) =
|
||||
|
||||
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
|
||||
@@ -580,32 +583,38 @@ type ExtendedGrid(grid: IGrid) =
|
||||
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
|
||||
)
|
||||
|> Array.fold
|
||||
(fun (a: int option) e ->
|
||||
if a.IsNone then
|
||||
let vx = this.Grid.getCellVertices e
|
||||
if Util.isInsideTriangle vx p then Some n else None
|
||||
else
|
||||
a
|
||||
)
|
||||
None
|
||||
)
|
||||
|
||||
member private this.tryFindElementTwice (grid: IGrid) (tree: IdxTree) ((p0, p1): float * float as p) =
|
||||
Util.tryFindElement grid tree p
|
||||
|> Option.orElse (
|
||||
tree.GetNearestNeighbours([| p0; p1 |], 1)
|
||||
tree.GetNearestNeighbours ([| p0; p1 |], 1)
|
||||
|> Array.tryHead
|
||||
|> Option.bind (fun leaf ->
|
||||
this.getElemsSurroundingElem leaf.Value
|
||||
|> Array.tryFind (fun eIdx ->
|
||||
let vx = this.Grid.getCellVertices eIdx
|
||||
Util.isInsideTriangle vx p))
|
||||
Util.isInsideTriangle vx p
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
member this.tryGetElement p =
|
||||
elementTree
|
||||
|> Option.bind (fun tree ->
|
||||
this.tryFindElementTwice grid tree p)
|
||||
|> Option.bind (fun tree -> this.tryFindElementTwice grid tree p)
|
||||
|> Option.orElseWith (fun () ->
|
||||
this.initElementTree ()
|
||||
this.tryFindElementTwice grid elementTree.Value p)
|
||||
this.tryFindElementTwice grid elementTree.Value p
|
||||
)
|
||||
|
||||
member this.tryGetElementSloppy(p0, p1 as p) =
|
||||
match elementTree with
|
||||
@@ -621,18 +630,18 @@ type ExtendedGrid(grid: IGrid) =
|
||||
|
||||
member this.getHash() =
|
||||
if gridHash.Length = 0 then
|
||||
let bg : BinGrid = {
|
||||
let bg: BinGrid = {
|
||||
hash = [||]
|
||||
vertices = (this :> IGrid).getVertices ()
|
||||
cells = (this :> IGrid).getCells ()
|
||||
}
|
||||
let bytes = MessagePackSerializer.Serialize(bg)
|
||||
let sha1 = System.Security.Cryptography.SHA1.Create()
|
||||
let bytes = MessagePackSerializer.Serialize (bg)
|
||||
let sha1 = System.Security.Cryptography.SHA1.Create ()
|
||||
gridHash <- sha1.ComputeHash bytes
|
||||
gridHash
|
||||
|
||||
member this.getHashString() =
|
||||
this.getHash() |> Convert.ToHexStringLower
|
||||
this.getHash () |> Convert.ToHexStringLower
|
||||
|
||||
member this.getCentroids() =
|
||||
match centroids with
|
||||
@@ -647,12 +656,10 @@ type ExtendedGrid(grid: IGrid) =
|
||||
Util.Element.calcCircumscribedCircle triangle
|
||||
|
||||
member this.getElemsSurroundingNode n =
|
||||
getNeighborIdx ()
|
||||
|> fun idx -> idx.ElemsAroundNode[n]
|
||||
getNeighborIdx () |> fun idx -> idx.ElemsAroundNode[n]
|
||||
|
||||
member this.getNodesSurroundingNode n =
|
||||
getNeighborIdx ()
|
||||
|> fun idx -> idx.NodesAroundNode[n]
|
||||
getNeighborIdx () |> fun idx -> idx.NodesAroundNode[n]
|
||||
|
||||
member this.getNodesSurroundingElem e =
|
||||
let idx = getNeighborIdx ()
|
||||
@@ -665,35 +672,34 @@ type ExtendedGrid(grid: IGrid) =
|
||||
getSurrounding idx.ElemsAroundNode elem
|
||||
|
||||
member this.saveNeighborIndex(fname: string) =
|
||||
let binarySerializer = FsPickler.CreateBinarySerializer()
|
||||
let binarySerializer = FsPickler.CreateBinarySerializer ()
|
||||
let nix = getNeighborIdx ()
|
||||
let pickle = binarySerializer.Pickle nix
|
||||
IO.File.WriteAllBytes(fname, pickle)
|
||||
IO.File.WriteAllBytes (fname, pickle)
|
||||
|
||||
member this.loadNeighborIndex(fname: string) =
|
||||
let binarySerializer = FsPickler.CreateBinarySerializer()
|
||||
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 this.saveNodeTree(fname: string) =
|
||||
let binarySerializer = FsPickler.CreateBinarySerializer()
|
||||
let binarySerializer = FsPickler.CreateBinarySerializer ()
|
||||
let tree =
|
||||
nodeTree
|
||||
|> Option.defaultWith (fun () ->
|
||||
this.initNodeTree ()
|
||||
nodeTree.Value)
|
||||
nodeTree.Value
|
||||
)
|
||||
|
||||
let pickle = binarySerializer.Pickle tree
|
||||
IO.File.WriteAllBytes(fname, pickle)
|
||||
IO.File.WriteAllBytes (fname, pickle)
|
||||
|
||||
member this.loadNodeTree(fname: string) =
|
||||
let binarySerializer = FsPickler.CreateBinarySerializer()
|
||||
let binarySerializer = FsPickler.CreateBinarySerializer ()
|
||||
if IO.File.Exists fname then
|
||||
let pickle = IO.File.ReadAllBytes fname
|
||||
nodeTree <- binarySerializer.UnPickle<IdxTree> pickle |> Some
|
||||
@@ -702,18 +708,19 @@ type ExtendedGrid(grid: IGrid) =
|
||||
false
|
||||
|
||||
member this.saveElementTree(fname: string) =
|
||||
let binarySerializer = FsPickler.CreateBinarySerializer()
|
||||
let binarySerializer = FsPickler.CreateBinarySerializer ()
|
||||
let tree =
|
||||
elementTree
|
||||
|> Option.defaultWith (fun () ->
|
||||
this.initElementTree ()
|
||||
elementTree.Value)
|
||||
elementTree.Value
|
||||
)
|
||||
|
||||
let pickle = binarySerializer.Pickle tree
|
||||
IO.File.WriteAllBytes(fname, pickle)
|
||||
IO.File.WriteAllBytes (fname, pickle)
|
||||
|
||||
member this.loadElementTree(fname: string) =
|
||||
let binarySerializer = FsPickler.CreateBinarySerializer()
|
||||
let binarySerializer = FsPickler.CreateBinarySerializer ()
|
||||
if IO.File.Exists fname then
|
||||
let pickle = IO.File.ReadAllBytes fname
|
||||
elementTree <- binarySerializer.UnPickle<IdxTree> pickle |> Some
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<IsPackable>true</IsPackable>
|
||||
<PackageId>Oceanbox.FvcomKit</PackageId>
|
||||
<RuntimeIdentifier>linux-x64</RuntimeIdentifier>
|
||||
<Authors/>
|
||||
<Company/>
|
||||
<Version>5.12.2</Version>
|
||||
|
||||
Reference in New Issue
Block a user