feat: Add ElemsAroundElem to NeighborIndex
Also format with Fantomas
This commit is contained in:
@@ -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
|
||||||
|
|||||||
16
shell.nix
16
shell.nix
@@ -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;
|
|
||||||
'';
|
|
||||||
}
|
}
|
||||||
|
|||||||
331
src/Grid.fs
331
src/Grid.fs
@@ -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
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user