feat: Add ElemsAroundElem to NeighborIndex

Also format with Fantomas
This commit is contained in:
2025-11-05 11:39:57 +01:00
parent bec03ed5ec
commit 71e861e417
4 changed files with 185 additions and 168 deletions

View File

@@ -11,6 +11,10 @@ insert_final_newline = false
indent_size = 2 indent_size = 2
max_line_length= 80 max_line_length= 80
[*.nix]
indent_size = 2
max_line_length= 80
[*.fs] [*.fs]
max_line_length= 120 max_line_length= 120
@@ -21,6 +25,7 @@ fsharp_space_before_uppercase_invocation = true
fsharp_blank_lines_around_nested_multiline_expressions = false fsharp_blank_lines_around_nested_multiline_expressions = false
fsharp_newline_between_type_definition_and_members = false fsharp_newline_between_type_definition_and_members = false
fsharp_multiline_bracket_style = stroustrup fsharp_multiline_bracket_style = stroustrup
fsharp_multi_line_lambda_closing_newline = true
fsharp_array_or_list_multiline_formatter = character_width fsharp_array_or_list_multiline_formatter = character_width
fsharp_max_array_or_list_width = 70 fsharp_max_array_or_list_width = 70

View File

@@ -2,15 +2,19 @@ with import <nixpkgs> { };
let let
dotnet-sdk = dotnet-sdk_9; dotnet-sdk = dotnet-sdk_9;
in in
mkShell { mkShell rec {
buildInputs = [ packages = [
netcdf
bun bun
dotnet-sdk dotnet-sdk
fsautocomplete
fantomas
];
buildInputs = [
netcdf
stdenv.cc.cc.lib
]; ];
DOTNET_ROOT = "${dotnet-sdk}/share/dotnet"; DOTNET_ROOT = "${dotnet-sdk}/share/dotnet";
shellHook = '' LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath buildInputs;
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:${stdenv.cc.cc.lib}/lib;
'';
} }

View File

@@ -9,7 +9,7 @@ open ProjNet.FSharp
open MessagePack open MessagePack
open MBrace.FsPickler open MBrace.FsPickler
//open FsKDTree //open FsKDTree
open KdTree // C# version open KdTree // NOTE: C# version
open Types open Types
@@ -21,8 +21,8 @@ type Elem = NodeIdx * NodeIdx * NodeIdx
type Node = float * float type Node = float * float
type Pos = float * float type Pos = float * float
type Leaf<'a> = { Pos : Pos; Data : 'a } type Leaf<'a> = { Pos: Pos; Data: 'a }
type Field = (float * float) [] type Field = (float * float) array
type Cell = NodeIdx * NodeIdx * NodeIdx type Cell = NodeIdx * NodeIdx * NodeIdx
@@ -30,16 +30,15 @@ type IGrid =
abstract getVertex: int -> Vertex abstract getVertex: int -> Vertex
abstract getCell: int -> Cell abstract getCell: int -> Cell
abstract getCellVertices: int -> Vertex * Vertex * Vertex abstract getCellVertices: int -> Vertex * Vertex * Vertex
abstract getVertices: unit -> Vertex [] abstract getVertices: unit -> Vertex array
abstract getCells: unit -> Cell [] abstract getCells: unit -> Cell array
abstract getBoundingBox: unit -> BBox abstract getBoundingBox: unit -> BBox
type Grid = type Grid = {
{ Elem: Elem array
Elem: Elem array Nodes: Node array
Nodes: Node array BBox: BBox
BBox: BBox } with
}
interface IGrid with interface IGrid with
member this.getVertex n = this.Nodes[n] member this.getVertex n = this.Nodes[n]
member this.getCell n = this.Elem[n] member this.getCell n = this.Elem[n]
@@ -49,34 +48,30 @@ type Grid =
member this.getVertices() = this.Nodes member this.getVertices() = this.Nodes
member this.getCells() = this.Elem member this.getCells() = this.Elem
member this.getBoundingBox() = this.BBox member this.getBoundingBox() = this.BBox
static member empty = static member empty = { Elem = Array.empty; Nodes = Array.empty; BBox = BBox.empty }
{
Elem = Array.empty
Nodes = Array.empty
BBox = BBox.empty
}
type ElemsAroundNode = Map<NodeIdx, ElemIdx []> type ElemsAroundNode = Map<NodeIdx, ElemIdx array>
type NodesAroundNode = Map<NodeIdx, NodeIdx []> type NodesAroundNode = Map<NodeIdx, NodeIdx array>
type ElemsAroundElem = Map<ElemIdx, ElemIdx array>
type NeighborIndex = type NeighborIndex = {
{ ElemsAroundNode: ElemsAroundNode
ElemsAroundNode: ElemsAroundNode NodesAroundNode: NodesAroundNode
NodesAroundNode: NodesAroundNode ElemsAroundElem: ElemsAroundElem
} } with
static member empty = { ElemsAroundNode = Map.empty; NodesAroundNode = Map.empty } static member empty = { ElemsAroundNode = Map.empty; NodesAroundNode = Map.empty; ElemsAroundElem = Map.empty }
type private Ean = Map<NodeIdx, ElemIdx list> type private Ean = Map<NodeIdx, ElemIdx list>
// NOTE(SimenLK): The amount of items to be stored in the trees leafs // 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<int> []) = let private createTree (points: Leaf<int> array) =
let tree = KdTree<float, int>(2, KdTree.Math.DoubleMath()) let tree = KdTree<float, int> (2, KdTree.Math.DoubleMath ())
points 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 if points.Length > 0 then
tree.Balance() tree.Balance ()
else else
Log.Warning $"Empty kd-tree" Log.Warning $"Empty kd-tree"
tree tree
@@ -89,12 +84,9 @@ let private makeElemsSurroundingNodeMap (elem: Elem array) : ElemsAroundNode =
elem elem
|> Array.fold |> Array.fold
(fun (n, acc) (a, b, c) -> (fun (n, acc) (a, b, c) ->
let acc' = let acc' = acc |> addElIdx a n |> addElIdx b n |> addElIdx c n
acc n + 1, acc'
|> addElIdx a n )
|> addElIdx b n
|> addElIdx c n
n + 1, acc')
(0, Map.empty) (0, Map.empty)
|> snd |> snd
|> Map.mapValues toArray |> Map.mapValues toArray
@@ -108,12 +100,9 @@ let private makeElemsSurroundingNodeMap' (elem: Elem array) : ElemsAroundNode =
elem elem
|> Array.fold |> Array.fold
(fun (n, acc) (a, b, c) -> (fun (n, acc) (a, b, c) ->
let acc' = let acc' = acc |> addElemIdx a n |> addElemIdx b n |> addElemIdx c n
acc n + 1, acc'
|> addElemIdx a n )
|> addElemIdx b n
|> addElemIdx c n
n + 1, acc')
(0, Map.empty) (0, Map.empty)
|> snd |> snd
|> Map.mapValues toArray |> Map.mapValues toArray
@@ -124,13 +113,26 @@ let private makeNodesSurroudingNodeMap (n2e: ElemsAroundNode) (elem: Elem array)
n n
|> Array.collect (fun x -> |> Array.collect (fun x ->
let n1, n2, n3 = elem[x] let n1, n2, n3 = elem[x]
[| n1; n2; n3 |]) [| n1; n2; n3 |]
|> Array.distinct) )
|> Array.distinct
)
let getSurrounding (idx: Map<int, int []>) (a, b, c) = let private makeElemsSurroundingElemMap (ean: ElemsAroundNode) (elem: Elem array) : ElemsAroundElem =
[| idx[a]; idx[b]; idx[c] |] elem
|> Array.concat |> Array.mapi (fun elemIdx (n1, n2, n3) ->
|> Array.distinct // 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 makeNeighborIndex (grid: IGrid) =
let elem = grid.getCells () let elem = grid.getCells ()
@@ -138,14 +140,17 @@ let makeNeighborIndex (grid: IGrid) =
{ {
ElemsAroundNode = ean ElemsAroundNode = ean
NodesAroundNode = makeNodesSurroudingNodeMap ean elem NodesAroundNode = makeNodesSurroudingNodeMap ean elem
ElemsAroundElem = makeElemsSurroundingElemMap ean elem
} }
let getElemsSurroundingNode (idx: NeighborIndex) n = idx.ElemsAroundNode[n] let getElemsSurroundingNode (idx: NeighborIndex) n = idx.ElemsAroundNode[n]
let getNodesSurroundingNode (idx: NeighborIndex) n = idx.NodesAroundNode[n] let getNodesSurroundingNode (idx: NeighborIndex) n = idx.NodesAroundNode[n]
let getNodesSurroundingElem (idx: NeighborIndex) (grid: Grid) e = let getNodesSurroundingElem (idx: NeighborIndex) e = idx.ElemsAroundElem[e]
getSurrounding idx.NodesAroundNode grid.Elem[e]
// let getNodesSurroundingElem (idx: NeighborIndex) (grid: Grid) e =
// getSurrounding idx.NodesAroundNode grid.Elem[e]
let getElemsSurroundingElem (idx: NeighborIndex) (grid: Grid) e = let getElemsSurroundingElem (idx: NeighborIndex) (grid: Grid) e =
getSurrounding idx.ElemsAroundNode grid.Elem[e] getSurrounding idx.ElemsAroundNode grid.Elem[e]
@@ -182,69 +187,68 @@ let bboxToLngLat (coordsys: CoordinateSystem) b =
let projectBBox proj b = let projectBBox proj b =
let x0, y0 = proj (b.minX, b.minY) let x0, y0 = proj (b.minX, b.minY)
let x1, y1 = proj (b.maxX, b.maxY) 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 = let projectGrid proj (grid: Grid) : Grid = {
{ grid with grid with
Nodes = grid.Nodes |> Array.Parallel.map proj Nodes = grid.Nodes |> Array.Parallel.map proj
BBox = projectBBox proj grid.BBox BBox = projectBBox proj grid.BBox
} }
let rescaleGrid (factor: float) (grid: Grid) : Grid = let rescaleGrid (factor: float) (grid: Grid) : Grid =
let nodes' = grid.Nodes |> Array.Parallel.map (fun (x, y) -> factor * x, factor * y) let nodes' = grid.Nodes |> Array.Parallel.map (fun (x, y) -> factor * x, factor * y)
let bbox' = calcBBox nodes' let bbox' = calcBBox nodes'
{ grid with { grid with Nodes = nodes'; BBox = bbox' }
Nodes = nodes'
BBox = bbox'
}
let translateGrid (x0, y0) grid = let translateGrid (x0, y0) grid =
let nodes' = grid.Nodes |> Array.Parallel.map (fun (x, y) -> x + x0, y + y0) let nodes' = grid.Nodes |> Array.Parallel.map (fun (x, y) -> x + x0, y + y0)
let bbox' = calcBBox nodes' let bbox' = calcBBox nodes'
{ grid with { grid with Nodes = nodes'; BBox = bbox' }
Nodes = nodes'
BBox = bbox'
}
let toWebMercator (coordsys: CoordinateSystem) (grid: Grid) = let toWebMercator (coordsys: CoordinateSystem) (grid: Grid) =
let toWebMercator = makeTransform coordsys CoordSys.EPSG3857 let toWebMercator = makeTransform coordsys CoordSys.EPSG3857
let s = System.Diagnostics.Stopwatch.StartNew() let s = System.Diagnostics.Stopwatch.StartNew ()
let g = grid |> projectGrid toWebMercator.project let g = grid |> projectGrid toWebMercator.project
s.Stop() s.Stop ()
Log.Debug $"Reprojected grid: {s.ElapsedMilliseconds} ms" Log.Debug $"Reprojected grid: {s.ElapsedMilliseconds} ms"
g g
let toLonLat (coordsys: CoordinateSystem) (grid: Grid) = let toLonLat (coordsys: CoordinateSystem) (grid: Grid) =
let toLonLat = makeTransform coordsys CoordSys.WGS84 let toLonLat = makeTransform coordsys CoordSys.WGS84
let s = System.Diagnostics.Stopwatch.StartNew() let s = System.Diagnostics.Stopwatch.StartNew ()
let g = grid |> projectGrid toLonLat.project let g = grid |> projectGrid toLonLat.project
s.Stop() s.Stop ()
Log.Debug $"Reprojected grid: {s.ElapsedMilliseconds} ms" Log.Debug $"Reprojected grid: {s.ElapsedMilliseconds} ms"
g g
let private chomp (l: string) = l.Split ' ' |> Array.filter ((<>) "") let private chomp (l: string) = l.Split ' ' |> Array.filter ((<>) "")
// TODO: pattern match and compare sizes for more flex // 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 let parse x = (chomp x)[3] |> int
try try
let nodes = parse h[0] let nodes = parse h[0]
let nele = parse h[1] let nele = parse h[1]
Some(nodes, nele) Some (nodes, nele)
with with _ ->
| _ -> None None
let private readObcHeader (h: string) = let private readObcHeader (h: string) =
try try
(chomp h)[4] |> int |> Some (chomp h)[4] |> int |> Some
with with _ ->
| _ -> None None
let private reader (parser: string [] -> 'a) (f: string array) = let private reader (parser: string[] -> 'a) (f: string array) =
try try
f |> Array.map (chomp >> parser) |> Some f |> Array.map (chomp >> parser) |> Some
with with e ->
| e ->
Log.Error e.Message Log.Error e.Message
None None
@@ -282,13 +286,13 @@ let readGrdFile (filename: string) =
let elem = readElem els let elem = readElem els
let nodes = readNodes nds let nodes = readNodes nds
let toGrid e n = { Elem = e; Nodes = n; BBox = calcBBox n } let toGrid e n = { Elem = e; Nodes = n; BBox = calcBBox n }
toGrid <!> elem <*> nodes) toGrid <!> elem <*> nodes
)
let readObcFile (filename: string) = let readObcFile (filename: string) =
let f = System.IO.File.ReadAllLines filename let f = System.IO.File.ReadAllLines filename
let hdr, rest = Array.splitAt 1 f let hdr, rest = Array.splitAt 1 f
readObcHeader hdr[0] readObcHeader hdr[0] |> Option.bind (fun _ -> readObc rest)
|> Option.bind (fun _ -> readObc rest)
module Boundary = module Boundary =
let normalizeElement (a, b, c) = let normalizeElement (a, b, c) =
@@ -302,11 +306,8 @@ module Boundary =
match Map.tryFind edge a with match Map.tryFind edge a with
| Some v -> Map.add edge (n :: v) a | Some v -> Map.add edge (n :: v) a
| None -> Map.add edge [ n ] a | None -> Map.add edge [ n ] a
let appendEdges a (n, x: Edge []) = let appendEdges a (n, x: Edge[]) =
a a |> appendEdge (n, x[0]) |> appendEdge (n, x[1]) |> appendEdge (n, x[2])
|> appendEdge (n, x[0])
|> appendEdge (n, x[1])
|> appendEdge (n, x[2])
grid.Elem grid.Elem
|> Array.mapi normElIdx |> Array.mapi normElIdx
|> Array.fold appendEdges Map.empty |> Array.fold appendEdges Map.empty
@@ -324,8 +325,7 @@ module Boundary =
|> Map.mapValues (fun x -> List.head x, List.last x) |> Map.mapValues (fun x -> List.head x, List.last x)
let makeBoundaryByElementMap (edgeMap: Map<Edge, ElemIdx>) = let makeBoundaryByElementMap (edgeMap: Map<Edge, ElemIdx>) =
edgeMap edgeMap |> Map.fold (fun a k v -> Map.add v k a) Map.empty
|> Map.fold (fun a k v -> Map.add v k a) Map.empty
let getBoundaryNodesArray (edgeMap: Map<Edge, ElemIdx>) = let getBoundaryNodesArray (edgeMap: Map<Edge, ElemIdx>) =
edgeMap edgeMap
@@ -345,8 +345,7 @@ module Util =
(x0 + x1 + x2) / 3f, (y0 + y1 + y2) / 3f (x0 + x1 + x2) / 3f, (y0 + y1 + y2) / 3f
static member calcArea((x0, y0), (x1, y1), (x2, y2)) = static member calcArea((x0, y0), (x1, y1), (x2, y2)) =
x0 * (y1 - y2) + x1 * (y2 - y0) + x2 * (y0 - y1) x0 * (y1 - y2) + x1 * (y2 - y0) + x2 * (y0 - y1) |> (*) 0.5
|> (*) 0.5
// static member calcArea((x0, y0), (x1, y1), (x2, y2)) = // static member calcArea((x0, y0), (x1, y1), (x2, y2)) =
// x0 * (y1 - y2) + x1 * (y2 - y0) + x2 * (y0 - y1) // x0 * (y1 - y2) + x1 * (y2 - y0) + x2 * (y0 - y1)
@@ -364,20 +363,20 @@ module Util =
let Sy = det Tx T2 E |> (*) 0.5 let Sy = det Tx T2 E |> (*) 0.5
let a = det Tx Ty E let a = det Tx Ty E
let b = det Tx Ty T2 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 S2 = (Sx, Sy) |> square
let r = (b / a + S2 / (a * a)) |> sqrt let r = (b / a + S2 / (a * a)) |> sqrt
Sx / a, Sy / a, r 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 [| 0 .. nIdx.NodesAroundNode.Count - 1 |] // total number of nodes
|> Array.Parallel.map (fun i -> |> Array.Parallel.map (fun i ->
let ns = nIdx.ElemsAroundNode[i] let ns = nIdx.ElemsAroundNode[i]
ns ns |> Array.fold (fun a n -> s[n] + a) 0. |> (*) (1. / float ns.Length)
|> 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 [| 0 .. nIdx.NodesAroundNode.Count - 1 |] // total number of nodes
|> Array.Parallel.map (fun i -> |> Array.Parallel.map (fun i ->
let ns = nIdx.ElemsAroundNode[i] let ns = nIdx.ElemsAroundNode[i]
@@ -385,11 +384,13 @@ module Util =
|> Array.fold |> Array.fold
(fun a n -> (fun a n ->
let u, v = s[n] let u, v = s[n]
sqrt (u * u + v * v) + a) sqrt (u * u + v * v) + a
)
0. 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 [| 0 .. nIdx.NodesAroundNode.Count - 1 |] // total number of nodes
|> Array.Parallel.map (fun i -> |> Array.Parallel.map (fun i ->
let ns = nIdx.ElemsAroundNode[i] let ns = nIdx.ElemsAroundNode[i]
@@ -398,9 +399,11 @@ module Util =
|> Array.fold |> Array.fold
(fun (au, av) n -> (fun (au, av) n ->
let u, v = s[n] let u, v = s[n]
u + au, v + av) u + au, v + av
)
(0., 0.) (0., 0.)
|> fun (u, v) -> u / n, v / n) |> fun (u, v) -> u / n, v / n
)
type Node = type Node =
static member calcNodeControlArea (idx: NeighborIndex) (grid: IGrid) = static member calcNodeControlArea (idx: NeighborIndex) (grid: IGrid) =
@@ -419,8 +422,10 @@ module Util =
let centroid = Element.calcCentroid (p0, p1, p2) let centroid = Element.calcCentroid (p0, p1, p2)
let a1 = Element.calcArea (p0', p1', p2') let a1 = Element.calcArea (p0', p1', p2')
let a2 = Element.calcArea (p1', centroid, p2') let a2 = Element.calcArea (p1', centroid, p2')
a1 + a2) a1 + a2
|> Array.sum) )
|> Array.sum
)
static member calcNodeArea (idx: NeighborIndex) (grid: IGrid) = static member calcNodeArea (idx: NeighborIndex) (grid: IGrid) =
let nodes = grid.getVertices () let nodes = grid.getVertices ()
@@ -428,7 +433,8 @@ module Util =
|> Array.Parallel.map (fun n -> |> Array.Parallel.map (fun n ->
getElemsSurroundingNode idx n getElemsSurroundingNode idx n
|> Array.map (grid.getCellVertices >> Element.calcArea) |> Array.map (grid.getCellVertices >> Element.calcArea)
|> Array.sum) |> Array.sum
)
let calcCentroids (grid: IGrid) = let calcCentroids (grid: IGrid) =
let n = grid.getVertices () let n = grid.getVertices ()
@@ -437,12 +443,12 @@ module Util =
let p0 = n[a] let p0 = n[a]
let p1 = n[b] let p1 = n[b]
let p2 = n[c] let p2 = n[c]
Element.calcCentroid (p0, p1, p2)) Element.calcCentroid (p0, p1, p2)
)
let inline isInsideTriangle (x, y, z) p = let inline isInsideTriangle (x, y, z) p =
let sign (p1x, p1y) (p2x, p2y) (p3x, p3y) = let sign (p1x, p1y) (p2x, p2y) (p3x, p3y) =
(p1x - p3x) * (p2y - p3y) (p1x - p3x) * (p2y - p3y) - (p2x - p3x) * (p1y - p3y)
- (p2x - p3x) * (p1y - p3y)
let d1 = sign p x y let d1 = sign p x y
let d2 = sign p y z let d2 = sign p y z
@@ -458,7 +464,8 @@ module Util =
grid.getVertices () grid.getVertices ()
|> Array.mapi (fun i v -> |> Array.mapi (fun i v ->
let x, y = v let x, y = v
{ Pos = x, y; Data = i }) { Pos = x, y; Data = i }
)
// |> create2DTree treeLeafSize // |> create2DTree treeLeafSize
|> createTree |> createTree
@@ -466,12 +473,10 @@ module Util =
let buildNearestElementTree (grid: IGrid) = let buildNearestElementTree (grid: IGrid) =
grid.getCells () grid.getCells ()
|> Array.mapi (fun i _ -> |> Array.mapi (fun i _ ->
let pos = let pos = i |> grid.getCellVertices |> Element.calcCentroid
i // |> fun (x, y) -> { X = x; Y = y }
|> grid.getCellVertices { Pos = pos; Data = i }
|> Element.calcCentroid )
// |> fun (x, y) -> { X = x; Y = y }
{ Pos = pos; Data = i })
// |> create2DTree treeLeafSize // |> create2DTree treeLeafSize
|> createTree |> createTree
@@ -487,14 +492,15 @@ module Util =
// else None) // else None)
let tryFindElement (grid: IGrid) (tree: KdTree<float, int>) ((p0, p1): float * float) = let tryFindElement (grid: IGrid) (tree: KdTree<float, int>) ((p0, p1): float * float) =
tree.GetNearestNeighbours([| p0; p1 |], 1) tree.GetNearestNeighbours ([| p0; p1 |], 1)
|> Array.tryHead |> Array.tryHead
|> Option.bind (fun leaf -> |> Option.bind (fun leaf ->
let vx = grid.getCellVertices leaf.Value let vx = grid.getCellVertices leaf.Value
if isInsideTriangle vx (p0, p1) then if isInsideTriangle vx (p0, p1) then
Some leaf.Value Some leaf.Value
else else
None) None
)
// type private IdxTree = Tree<Leaf<single, ElemIdx> array, Node<single>> // type private IdxTree = Tree<Leaf<single, ElemIdx> array, Node<single>>
// type private NodeIdxTree = Tree<Leaf<single, NodeIdx> array, Node<single>> // type private NodeIdxTree = Tree<Leaf<single, NodeIdx> array, Node<single>>
@@ -503,22 +509,20 @@ type private NodeIdxTree = KdTree<float, int>
[<MessagePackObject>] [<MessagePackObject>]
type BinGrid = { type BinGrid = {
[<Key(0)>] hash : byte[] [<Key(0)>]
[<Key(1)>] vertices : (float * float)[] hash: byte[]
[<Key(2)>] cells : (int * int * int)[] [<Key(1)>]
vertices: (float * float)[]
[<Key(2)>]
cells: (int * int * int)[]
} with } with
member this.toGrid (): Grid = member this.toGrid() : Grid = { Nodes = this.vertices; Elem = this.cells; BBox = calcBBox this.vertices }
{
Nodes = this.vertices
Elem = this.cells
BBox = calcBBox this.vertices
}
type ExtendedGrid(grid: IGrid) = type ExtendedGrid(grid: IGrid) =
let mutable nodeTree: NodeIdxTree option = None let mutable nodeTree: NodeIdxTree option = None
let mutable elementTree: IdxTree option = None let mutable elementTree: IdxTree option = None
let mutable neighborIndex: NeighborIndex 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 mutable gridHash: byte[] = [||]
let getNeighborIdx () = let getNeighborIdx () =
@@ -542,12 +546,11 @@ type ExtendedGrid(grid: IGrid) =
member this.Grid = grid member this.Grid = grid
member this.ToGrid() = member this.ToGrid() = {
{ Nodes = this.Grid.getVertices ()
Nodes = this.Grid.getVertices () Elem = this.Grid.getCells ()
Elem = this.Grid.getCells () BBox = this.Grid.getBoundingBox ()
BBox = this.Grid.getBoundingBox () }
}
member this.initNeighborIndex(?cache: string) = member this.initNeighborIndex(?cache: string) =
@@ -567,7 +570,7 @@ type ExtendedGrid(grid: IGrid) =
member this.nearestNode(p0: float, p1: float) = member this.nearestNode(p0: float, p1: float) =
let nearest (tree: KdTree<_, _>) = let nearest (tree: KdTree<_, _>) =
tree.GetNearestNeighbours([| p0; p1 |], 1) tree.GetNearestNeighbours ([| p0; p1 |], 1)
|> Array.tryHead |> Array.tryHead
|> Option.map (fun l -> l.Value) |> Option.map (fun l -> l.Value)
match nodeTree with match nodeTree with
@@ -580,32 +583,38 @@ type ExtendedGrid(grid: IGrid) =
this.nearestNode p this.nearestNode p
|> Option.bind (fun n -> |> Option.bind (fun n ->
this.getElemsSurroundingNode n this.getElemsSurroundingNode n
|> Array.fold (fun (a: int option) e -> |> Array.fold
if a.IsNone then (fun (a: int option) e ->
let vx = this.Grid.getCellVertices e if a.IsNone then
if Util.isInsideTriangle vx p then Some n else None let vx = this.Grid.getCellVertices e
else a) None 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) = member private this.tryFindElementTwice (grid: IGrid) (tree: IdxTree) ((p0, p1): float * float as p) =
Util.tryFindElement grid tree p Util.tryFindElement grid tree p
|> Option.orElse ( |> Option.orElse (
tree.GetNearestNeighbours([| p0; p1 |], 1) tree.GetNearestNeighbours ([| p0; p1 |], 1)
|> Array.tryHead |> Array.tryHead
|> Option.bind (fun leaf -> |> Option.bind (fun leaf ->
this.getElemsSurroundingElem leaf.Value this.getElemsSurroundingElem leaf.Value
|> Array.tryFind (fun eIdx -> |> Array.tryFind (fun eIdx ->
let vx = this.Grid.getCellVertices eIdx let vx = this.Grid.getCellVertices eIdx
Util.isInsideTriangle vx p)) Util.isInsideTriangle vx p
)
) )
)
member this.tryGetElement p = member this.tryGetElement p =
elementTree elementTree
|> Option.bind (fun tree -> |> Option.bind (fun tree -> this.tryFindElementTwice grid tree p)
this.tryFindElementTwice grid tree p)
|> Option.orElseWith (fun () -> |> Option.orElseWith (fun () ->
this.initElementTree () this.initElementTree ()
this.tryFindElementTwice grid elementTree.Value p) this.tryFindElementTwice grid elementTree.Value p
)
member this.tryGetElementSloppy(p0, p1 as p) = member this.tryGetElementSloppy(p0, p1 as p) =
match elementTree with match elementTree with
@@ -621,18 +630,18 @@ type ExtendedGrid(grid: IGrid) =
member this.getHash() = member this.getHash() =
if gridHash.Length = 0 then if gridHash.Length = 0 then
let bg : BinGrid = { let bg: BinGrid = {
hash = [||] hash = [||]
vertices = (this :> IGrid).getVertices () vertices = (this :> IGrid).getVertices ()
cells = (this :> IGrid).getCells () cells = (this :> IGrid).getCells ()
} }
let bytes = MessagePackSerializer.Serialize(bg) let bytes = MessagePackSerializer.Serialize (bg)
let sha1 = System.Security.Cryptography.SHA1.Create() let sha1 = System.Security.Cryptography.SHA1.Create ()
gridHash <- sha1.ComputeHash bytes gridHash <- sha1.ComputeHash bytes
gridHash gridHash
member this.getHashString() = member this.getHashString() =
this.getHash() |> Convert.ToHexStringLower this.getHash () |> Convert.ToHexStringLower
member this.getCentroids() = member this.getCentroids() =
match centroids with match centroids with
@@ -647,12 +656,10 @@ type ExtendedGrid(grid: IGrid) =
Util.Element.calcCircumscribedCircle triangle Util.Element.calcCircumscribedCircle triangle
member this.getElemsSurroundingNode n = member this.getElemsSurroundingNode n =
getNeighborIdx () getNeighborIdx () |> fun idx -> idx.ElemsAroundNode[n]
|> fun idx -> idx.ElemsAroundNode[n]
member this.getNodesSurroundingNode n = member this.getNodesSurroundingNode n =
getNeighborIdx () getNeighborIdx () |> fun idx -> idx.NodesAroundNode[n]
|> fun idx -> idx.NodesAroundNode[n]
member this.getNodesSurroundingElem e = member this.getNodesSurroundingElem e =
let idx = getNeighborIdx () let idx = getNeighborIdx ()
@@ -665,35 +672,34 @@ type ExtendedGrid(grid: IGrid) =
getSurrounding idx.ElemsAroundNode elem getSurrounding idx.ElemsAroundNode elem
member this.saveNeighborIndex(fname: string) = member this.saveNeighborIndex(fname: string) =
let binarySerializer = FsPickler.CreateBinarySerializer() let binarySerializer = FsPickler.CreateBinarySerializer ()
let nix = getNeighborIdx () let nix = getNeighborIdx ()
let pickle = binarySerializer.Pickle nix let pickle = binarySerializer.Pickle nix
IO.File.WriteAllBytes(fname, pickle) IO.File.WriteAllBytes (fname, pickle)
member this.loadNeighborIndex(fname: string) = member this.loadNeighborIndex(fname: string) =
let binarySerializer = FsPickler.CreateBinarySerializer() let binarySerializer = FsPickler.CreateBinarySerializer ()
if IO.File.Exists fname then if IO.File.Exists fname then
let pickle = IO.File.ReadAllBytes fname let pickle = IO.File.ReadAllBytes fname
neighborIndex <- neighborIndex <- binarySerializer.UnPickle<NeighborIndex> pickle |> Some
binarySerializer.UnPickle<NeighborIndex> pickle
|> Some
true true
else else
false false
member this.saveNodeTree(fname: string) = member this.saveNodeTree(fname: string) =
let binarySerializer = FsPickler.CreateBinarySerializer() let binarySerializer = FsPickler.CreateBinarySerializer ()
let tree = let tree =
nodeTree nodeTree
|> Option.defaultWith (fun () -> |> Option.defaultWith (fun () ->
this.initNodeTree () this.initNodeTree ()
nodeTree.Value) nodeTree.Value
)
let pickle = binarySerializer.Pickle tree let pickle = binarySerializer.Pickle tree
IO.File.WriteAllBytes(fname, pickle) IO.File.WriteAllBytes (fname, pickle)
member this.loadNodeTree(fname: string) = member this.loadNodeTree(fname: string) =
let binarySerializer = FsPickler.CreateBinarySerializer() let binarySerializer = FsPickler.CreateBinarySerializer ()
if IO.File.Exists fname then if IO.File.Exists fname then
let pickle = IO.File.ReadAllBytes fname let pickle = IO.File.ReadAllBytes fname
nodeTree <- binarySerializer.UnPickle<IdxTree> pickle |> Some nodeTree <- binarySerializer.UnPickle<IdxTree> pickle |> Some
@@ -702,18 +708,19 @@ type ExtendedGrid(grid: IGrid) =
false false
member this.saveElementTree(fname: string) = member this.saveElementTree(fname: string) =
let binarySerializer = FsPickler.CreateBinarySerializer() let binarySerializer = FsPickler.CreateBinarySerializer ()
let tree = let tree =
elementTree elementTree
|> Option.defaultWith (fun () -> |> Option.defaultWith (fun () ->
this.initElementTree () this.initElementTree ()
elementTree.Value) elementTree.Value
)
let pickle = binarySerializer.Pickle tree let pickle = binarySerializer.Pickle tree
IO.File.WriteAllBytes(fname, pickle) IO.File.WriteAllBytes (fname, pickle)
member this.loadElementTree(fname: string) = member this.loadElementTree(fname: string) =
let binarySerializer = FsPickler.CreateBinarySerializer() let binarySerializer = FsPickler.CreateBinarySerializer ()
if IO.File.Exists fname then if IO.File.Exists fname then
let pickle = IO.File.ReadAllBytes fname let pickle = IO.File.ReadAllBytes fname
elementTree <- binarySerializer.UnPickle<IdxTree> pickle |> Some elementTree <- binarySerializer.UnPickle<IdxTree> pickle |> Some

View File

@@ -5,6 +5,7 @@
<TargetFramework>net9.0</TargetFramework> <TargetFramework>net9.0</TargetFramework>
<IsPackable>true</IsPackable> <IsPackable>true</IsPackable>
<PackageId>Oceanbox.FvcomKit</PackageId> <PackageId>Oceanbox.FvcomKit</PackageId>
<RuntimeIdentifier>linux-x64</RuntimeIdentifier>
<Authors/> <Authors/>
<Company/> <Company/>
<Version>5.12.2</Version> <Version>5.12.2</Version>