From f1400fdcb534ccaec50d5b7e9c41b4fd30d283ea Mon Sep 17 00:00:00 2001 From: Marcel Schurer Date: Fri, 17 Oct 2025 16:03:25 +0200 Subject: [PATCH 001/134] Update vtk2trixi function to return `NamedTuple` instead of `InitialCondition` --- src/io/read_vtk.jl | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/src/io/read_vtk.jl b/src/io/read_vtk.jl index 90daa721a3..99d049fe8d 100644 --- a/src/io/read_vtk.jl +++ b/src/io/read_vtk.jl @@ -1,7 +1,7 @@ """ vtk2trixi(file::String) -Load VTK file and convert data to an [`InitialCondition`](@ref). +Load VTK file and convert data to an NamedTuple. # Arguments - `file`: Name of the VTK file to be loaded. @@ -18,17 +18,11 @@ rectangular = RectangularShape(0.1, (10, 10), (0, 0), density=1.5, velocity=(1.0 # Write the `InitialCondition` to a vtk file trixi2vtk(rectangular; filename="rectangular", output_directory="out") -# Read the vtk file and convert it to `InitialCondition` -ic = vtk2trixi(joinpath("out", "rectangular.vtu")) +# Read the vtk file and convert the data to an `NamedTuple` +data = vtk2trixi(joinpath("out", "rectangular.vtu")) # output -┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ -│ InitialCondition{Float64} │ -│ ═════════════════════════ │ -│ #dimensions: ……………………………………………… 2 │ -│ #particles: ………………………………………………… 100 │ -│ particle spacing: ………………………………… 0.1 │ -└──────────────────────────────────────────────────────────────────────────────────────────────────┘ +NamedTuple{data...} """ function vtk2trixi(file) vtk_file = ReadVTK.VTKFile(file) @@ -39,6 +33,8 @@ function vtk2trixi(file) # Retrieve fields ndims = first(ReadVTK.get_data(field_data["ndims"])) + time = "time" in keys(field_data) ? first(ReadVTK.get_data(field_data["time"])) : 0.0 + coordinates = ReadVTK.get_points(vtk_file)[1:ndims, :] fields = ["velocity", "density", "pressure", "mass", "particle_spacing"] @@ -62,9 +58,10 @@ function vtk2trixi(file) first(results["particle_spacing"]) : results["particle_spacing"] - return InitialCondition(; coordinates, particle_spacing=particle_spacing, - velocity=results["velocity"], - mass=results["mass"], - density=results["density"], - pressure=results["pressure"]) + + return (time, coordinates, particle_spacing, + velocity=results["velocity"], + mass=results["mass"], + density=results["density"], + pressure=results["pressure"]) end From 217f71103261ea5a83a8e6984a021ffecf526b61 Mon Sep 17 00:00:00 2001 From: Marcel Schurer Date: Fri, 17 Oct 2025 18:08:43 +0200 Subject: [PATCH 002/134] Refactor vtk2trixi function to accept custom fields --- src/io/read_vtk.jl | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/io/read_vtk.jl b/src/io/read_vtk.jl index 99d049fe8d..66efb764ac 100644 --- a/src/io/read_vtk.jl +++ b/src/io/read_vtk.jl @@ -24,44 +24,44 @@ data = vtk2trixi(joinpath("out", "rectangular.vtu")) # output NamedTuple{data...} """ -function vtk2trixi(file) +function vtk2trixi(file; custom_fields...) vtk_file = ReadVTK.VTKFile(file) # Retrieve data fields (e.g., pressure, velocity, ...) point_data = ReadVTK.get_point_data(vtk_file) field_data = ReadVTK.get_field_data(vtk_file) + results = Dict{Symbol, Any}() + # Retrieve fields ndims = first(ReadVTK.get_data(field_data["ndims"])) - time = "time" in keys(field_data) ? first(ReadVTK.get_data(field_data["time"])) : 0.0 - coordinates = ReadVTK.get_points(vtk_file)[1:ndims, :] - fields = ["velocity", "density", "pressure", "mass", "particle_spacing"] - results = Dict{String, Array{Float64}}() - + fields = [:velocity, :density, :pressure, :mass, :particle_spacing] for field in fields # Look for any key that contains the field name all_keys = keys(point_data) - idx = findfirst(k -> occursin(field, k), all_keys) + idx = findfirst(k -> occursin(string(field), k), all_keys) if idx !== nothing results[field] = ReadVTK.get_data(point_data[all_keys[idx]]) else # Use zeros as default values when a field is missing - results[field] = field in ["mass"] ? - zeros(size(coordinates, 2)) : zero(coordinates) + results[field] = string(field) in ["mass"] ? + zeros(size(coordinates, 2)) : zero(coordinates) @info "No '$field' field found in VTK file. Will be set to zero." end end - particle_spacing = allequal(results["particle_spacing"]) ? - first(results["particle_spacing"]) : - results["particle_spacing"] + results[:particle_spacing] = allequal(results[:particle_spacing]) ? + first(results[:particle_spacing]) : + results[:particle_spacing] + results[:coordinates] = coordinates + results[:time] = "time" in keys(field_data) ? + first(ReadVTK.get_data(field_data["time"])) : 0.0 + for (key, field) in custom_fields + results[key] = first(ReadVTK.get_data(field_data[field])) + end - return (time, coordinates, particle_spacing, - velocity=results["velocity"], - mass=results["mass"], - density=results["density"], - pressure=results["pressure"]) + return NamedTuple(results) end From 0fa590038776711b3023bb5b82758c587e2a765d Mon Sep 17 00:00:00 2001 From: Marcel Schurer Date: Mon, 20 Oct 2025 17:32:41 +0200 Subject: [PATCH 003/134] Enhance vtk2trixi function to support scalar and vector custom quantities and update tests --- src/io/read_vtk.jl | 36 ++++++---- test/io/read_vtk.jl | 164 +++++++++++++++++++++++++++++++++----------- 2 files changed, 149 insertions(+), 51 deletions(-) diff --git a/src/io/read_vtk.jl b/src/io/read_vtk.jl index 66efb764ac..abd610a18b 100644 --- a/src/io/read_vtk.jl +++ b/src/io/read_vtk.jl @@ -1,10 +1,15 @@ """ - vtk2trixi(file::String) + vtk2trixi(file::String; custom_quantities...) Load VTK file and convert data to an NamedTuple. # Arguments -- `file`: Name of the VTK file to be loaded. +- `file`: Name of the VTK file to be loaded. +- `custom_quantities...`: Additional custom quantities to be loaded from the VTK file. + Each custom quantity must be explicitly listed in the + `custom_quantities` during the simulation to ensure it is + included in the VTK output and can be successfully loaded. + See [Custom Quantities](@ref custom_quantities) for details. !!! warning "Experimental Implementation" This is an experimental feature and may change in any future releases. @@ -15,16 +20,18 @@ Load VTK file and convert data to an NamedTuple. rectangular = RectangularShape(0.1, (10, 10), (0, 0), density=1.5, velocity=(1.0, -2.0), pressure=1000.0) -# Write the `InitialCondition` to a vtk file -trixi2vtk(rectangular; filename="rectangular", output_directory="out") +# Write the `InitialCondition` with custom quantity to a vtk file +trixi2vtk(rectangular; filename="rectangular", output_directory="out", + my_custom_quantity=3.0) # Read the vtk file and convert the data to an `NamedTuple` -data = vtk2trixi(joinpath("out", "rectangular.vtu")) +data = vtk2trixi(joinpath("out", "rectangular.vtu"); + my_custom_quantity="my_custom_quantity") # output NamedTuple{data...} """ -function vtk2trixi(file; custom_fields...) +function vtk2trixi(file; custom_quantities...) vtk_file = ReadVTK.VTKFile(file) # Retrieve data fields (e.g., pressure, velocity, ...) @@ -47,20 +54,25 @@ function vtk2trixi(file; custom_fields...) else # Use zeros as default values when a field is missing results[field] = string(field) in ["mass"] ? - zeros(size(coordinates, 2)) : zero(coordinates) + zeros(size(coordinates, 2)) : zero(coordinates) @info "No '$field' field found in VTK file. Will be set to zero." end end results[:particle_spacing] = allequal(results[:particle_spacing]) ? - first(results[:particle_spacing]) : - results[:particle_spacing] + first(results[:particle_spacing]) : + results[:particle_spacing] results[:coordinates] = coordinates results[:time] = "time" in keys(field_data) ? - first(ReadVTK.get_data(field_data["time"])) : 0.0 + first(ReadVTK.get_data(field_data["time"])) : 0.0 - for (key, field) in custom_fields - results[key] = first(ReadVTK.get_data(field_data[field])) + for (key, quantity) in custom_quantities + if quantity in keys(point_data) + results[key] = ReadVTK.get_data(point_data[quantity]) + end + if quantity in keys(field_data) + results[key] = first(ReadVTK.get_data(field_data[quantity])) + end end return NamedTuple(results) diff --git a/test/io/read_vtk.jl b/test/io/read_vtk.jl index d82e8c50c3..c603c597e0 100644 --- a/test/io/read_vtk.jl +++ b/test/io/read_vtk.jl @@ -3,19 +3,50 @@ coordinates = fill(1.0, 2, 12) velocity = fill(2.0, 2, 12) - expected_ic = InitialCondition(; coordinates=coordinates, velocity=velocity, + expected_ic = InitialCondition(coordinates=coordinates, velocity=velocity, density=1000.0, pressure=900.0, mass=50.0) - @testset verbose=true "`InitialCondition`" begin - trixi2vtk(expected_ic; filename="tmp_initial_condition", - output_directory=tmp_dir) - - test_ic = vtk2trixi(joinpath(tmp_dir, "tmp_initial_condition.vtu")) + expected_cq_scalar = 3.0 + expected_cq_vector = fill(expected_cq_scalar, + size(expected_ic.coordinates, 2)) + scalar_cq_function(system, data, t) = expected_cq_scalar + vector_cq_function(system, data, + t) = fill(expected_cq_scalar, nparticles(system)) - @test isapprox(expected_ic.coordinates, test_ic.coordinates, rtol=1e-5) - @test isapprox(expected_ic.velocity, test_ic.velocity, rtol=1e-5) - @test isapprox(expected_ic.density, test_ic.density, rtol=1e-5) - @test isapprox(expected_ic.pressure, test_ic.pressure, rtol=1e-5) + @testset verbose=true "`InitialCondition`" begin + @testset verbose=true "Scalar custom quantity" begin + trixi2vtk(expected_ic; filename="tmp_initial_condition_scalar", + output_directory=tmp_dir, + cq_scalar=expected_cq_scalar) + + # Load file containing test data + test_data = vtk2trixi(joinpath(tmp_dir, "tmp_initial_condition_scalar.vtu"); + cq_scalar="cq_scalar") + + @test isapprox(expected_ic.coordinates, test_data.coordinates, rtol=1e-5) + @test isapprox(expected_ic.velocity, test_data.velocity, rtol=1e-5) + @test isapprox(expected_ic.density, test_data.density, rtol=1e-5) + @test isapprox(expected_ic.pressure, test_data.pressure, rtol=1e-5) + @test isapprox(expected_cq_scalar, test_data.cq_scalar, + rtol=1e-5) + end + + @testset verbose=true "Vector custom quantity" begin + trixi2vtk(expected_ic; filename="tmp_initial_condition_vector", + output_directory=tmp_dir, + cq_vector=expected_cq_vector) + + # Load file containing test data + test_data = vtk2trixi(joinpath(tmp_dir, "tmp_initial_condition_vector.vtu"); + cq_vector="cq_vector") + + @test isapprox(expected_ic.coordinates, test_data.coordinates, rtol=1e-5) + @test isapprox(expected_ic.velocity, test_data.velocity, rtol=1e-5) + @test isapprox(expected_ic.density, test_data.density, rtol=1e-5) + @test isapprox(expected_ic.pressure, test_data.pressure, rtol=1e-5) + @test isapprox(expected_cq_vector, test_data.cq_vector, + rtol=1e-5) + end end @testset verbose=true "`AbstractFluidSystem`" begin @@ -29,27 +60,52 @@ semi = Semidiscretization(fluid_system) # Create random ODE solutions - dvdu_ode = nothing v = fill(2.0, ndims(fluid_system), nparticles(fluid_system)) pressure = fill(3.0, nparticles(fluid_system)) v_ode = vec([v; pressure']) u = fill(1.0, ndims(fluid_system), nparticles(fluid_system)) u_ode = vec(u) + dv_ode = zero(v_ode) + du_ode = zero(u_ode) x = (; v_ode, u_ode) vu_ode = (; x) - - # Write out `AbstractFluidSystem` Simulation-File - trixi2vtk(fluid_system, dvdu_ode, vu_ode, semi, 0.0, - nothing; system_name="tmp_file_fluid", output_directory=tmp_dir, - iter=1) - - # Load `AbstractFluidSystem` Simulation-File - test = vtk2trixi(joinpath(tmp_dir, "tmp_file_fluid_1.vtu")) - - @test isapprox(u, test.coordinates, rtol=1e-5) - @test isapprox(v, test.velocity, rtol=1e-5) - @test isapprox(pressure, test.pressure, rtol=1e-5) - @test isapprox(fluid_system.cache.density, test.density, rtol=1e-5) + dvdu_ode = (; x=(; v_ode=dv_ode, u_ode=du_ode)) + + @testset verbose=true "Scalar custom quantity" begin + trixi2vtk(fluid_system, dvdu_ode, vu_ode, semi, 0.0, + nothing; system_name="tmp_file_fluid_scalar", + output_directory=tmp_dir, + iter=1, cq_scalar=scalar_cq_function) + + # Load file containing test data + test_data = vtk2trixi(joinpath(tmp_dir, "tmp_file_fluid_scalar_1.vtu"); + cq_scalar="cq_scalar") + + @test isapprox(u, test_data.coordinates, rtol=1e-5) + @test isapprox(v, test_data.velocity, rtol=1e-5) + @test isapprox(pressure, test_data.pressure, rtol=1e-5) + @test isapprox(fluid_system.cache.density, test_data.density, rtol=1e-5) + @test isapprox(expected_cq_scalar, test_data.cq_scalar, + rtol=1e-5) + end + + @testset verbose=true "Vector custom quantity" begin + trixi2vtk(fluid_system, dvdu_ode, vu_ode, semi, 0.0, + nothing; system_name="tmp_file_fluid_vector", + output_directory=tmp_dir, + iter=1, cq_vector=vector_cq_function) + + # Load file containing test data + test_data = vtk2trixi(joinpath(tmp_dir, "tmp_file_fluid_vector_1.vtu"); + cq_vector="cq_vector") + + @test isapprox(u, test_data.coordinates, rtol=1e-5) + @test isapprox(v, test_data.velocity, rtol=1e-5) + @test isapprox(pressure, test_data.pressure, rtol=1e-5) + @test isapprox(fluid_system.cache.density, test_data.density, rtol=1e-5) + @test isapprox(fill(expected_cq_scalar, nparticles(fluid_system)), + test_data.cq_vector, rtol=1e-5) + end end @testset verbose=true "`WallBoundarySystem`" begin @@ -67,25 +123,55 @@ semi = Semidiscretization(boundary_system) # Create dummy ODE solutions - dvdu_ode = nothing v_ode = zeros(ndims(boundary_system) * nparticles(boundary_system)) u_ode = zeros(ndims(boundary_system) * nparticles(boundary_system)) + dv_ode = zero(v_ode) + du_ode = zero(u_ode) x = (; v_ode, u_ode) vu_ode = (; x) - - # Write out `WallBoundarySystem` Simulation-File - trixi2vtk(boundary_system, dvdu_ode, vu_ode, semi, 0.0, - nothing; system_name="tmp_file_boundary", output_directory=tmp_dir, - iter=1) - - # Load `WallBoundarySystem` Simulation-File - test = vtk2trixi(joinpath(tmp_dir, "tmp_file_boundary_1.vtu")) - - @test isapprox(boundary_system.coordinates, test.coordinates, rtol=1e-5) - # The velocity is always zero for `WallBoundarySystem` - @test isapprox(zeros(size(test.velocity)), test.velocity, rtol=1e-5) - @test isapprox(boundary_model.pressure, test.pressure, rtol=1e-5) - @test isapprox(boundary_model.cache.density, test.density, rtol=1e-5) + dvdu_ode = (; x=(; v_ode=dv_ode, u_ode=du_ode)) + + @testset verbose=true "Scalar custom quantity" begin + trixi2vtk(boundary_system, dvdu_ode, vu_ode, semi, 0.0, + nothing; system_name="tmp_file_boundary_scalar", + output_directory=tmp_dir, + iter=1, cq_scalar=scalar_cq_function) + + # Load file containing test data + test_data = vtk2trixi(joinpath(tmp_dir, "tmp_file_boundary_scalar_1.vtu"); + cq_scalar="cq_scalar") + + @test isapprox(boundary_system.coordinates, test_data.coordinates, + rtol=1e-5) + # The velocity is always zero for `WallBoundarySystem` + @test isapprox(zeros(size(test_data.velocity)), test_data.velocity, + rtol=1e-5) + @test isapprox(boundary_model.pressure, test_data.pressure, rtol=1e-5) + @test isapprox(boundary_model.cache.density, test_data.density, rtol=1e-5) + @test isapprox(expected_cq_scalar, test_data.cq_scalar, + rtol=1e-5) + end + + @testset verbose=true "Vector custom quantity" begin + trixi2vtk(boundary_system, dvdu_ode, vu_ode, semi, 0.0, + nothing; system_name="tmp_file_boundary_vector", + output_directory=tmp_dir, + iter=1, cq_vector=vector_cq_function) + + # Load file containing test data + test_data = vtk2trixi(joinpath(tmp_dir, "tmp_file_boundary_vector_1.vtu"); + cq_vector="cq_vector") + + @test isapprox(boundary_system.coordinates, test_data.coordinates, + rtol=1e-5) + # The velocity is always zero for `WallBoundarySystem` + @test isapprox(zeros(size(test_data.velocity)), test_data.velocity, + rtol=1e-5) + @test isapprox(boundary_model.pressure, test_data.pressure, rtol=1e-5) + @test isapprox(boundary_model.cache.density, test_data.density, rtol=1e-5) + @test isapprox(fill(expected_cq_scalar, nparticles(boundary_system)), + test_data.cq_vector, rtol=1e-5) + end end end end From 38a9b2aebfdf845a357fa903ad02d0380f5b7fe3 Mon Sep 17 00:00:00 2001 From: Marcel Schurer Date: Tue, 21 Oct 2025 14:31:30 +0200 Subject: [PATCH 004/134] Fix typo in documentation for vtk2trixi function --- src/io/read_vtk.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/io/read_vtk.jl b/src/io/read_vtk.jl index abd610a18b..e189d81bfc 100644 --- a/src/io/read_vtk.jl +++ b/src/io/read_vtk.jl @@ -1,7 +1,7 @@ """ vtk2trixi(file::String; custom_quantities...) -Load VTK file and convert data to an NamedTuple. +Load VTK file and convert data to a NamedTuple. # Arguments - `file`: Name of the VTK file to be loaded. From 5d1192aaa014c3c47e7d318b5fce22b9b607e15a Mon Sep 17 00:00:00 2001 From: Marcel Schurer Date: Thu, 23 Oct 2025 11:30:38 +0200 Subject: [PATCH 005/134] Fix capitalization in comments --- src/io/read_vtk.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/io/read_vtk.jl b/src/io/read_vtk.jl index e189d81bfc..02e341f1ae 100644 --- a/src/io/read_vtk.jl +++ b/src/io/read_vtk.jl @@ -20,11 +20,11 @@ Load VTK file and convert data to a NamedTuple. rectangular = RectangularShape(0.1, (10, 10), (0, 0), density=1.5, velocity=(1.0, -2.0), pressure=1000.0) -# Write the `InitialCondition` with custom quantity to a vtk file +# Write the `InitialCondition` with custom quantity to a VTK file trixi2vtk(rectangular; filename="rectangular", output_directory="out", my_custom_quantity=3.0) -# Read the vtk file and convert the data to an `NamedTuple` +# Read the VTK file and convert the data to a `NamedTuple` data = vtk2trixi(joinpath("out", "rectangular.vtu"); my_custom_quantity="my_custom_quantity") From 7e51a814bfc6e2d7ea693f96d0724bfa8e0d73aa Mon Sep 17 00:00:00 2001 From: LasNikas Date: Sat, 22 Nov 2025 08:54:34 +0100 Subject: [PATCH 006/134] add ELTYPE --- src/io/read_vtk.jl | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/io/read_vtk.jl b/src/io/read_vtk.jl index 90daa721a3..d87a586bf2 100644 --- a/src/io/read_vtk.jl +++ b/src/io/read_vtk.jl @@ -29,8 +29,10 @@ ic = vtk2trixi(joinpath("out", "rectangular.vtu")) │ #particles: ………………………………………………… 100 │ │ particle spacing: ………………………………… 0.1 │ └──────────────────────────────────────────────────────────────────────────────────────────────────┘ +``` """ -function vtk2trixi(file) +function vtk2trixi(file; element_type=Float64) + ELTYPE = element_type vtk_file = ReadVTK.VTKFile(file) # Retrieve data fields (e.g., pressure, velocity, ...) @@ -39,7 +41,7 @@ function vtk2trixi(file) # Retrieve fields ndims = first(ReadVTK.get_data(field_data["ndims"])) - coordinates = ReadVTK.get_points(vtk_file)[1:ndims, :] + coordinates = convert.(ELTYPE, ReadVTK.get_points(vtk_file)[1:ndims, :]) fields = ["velocity", "density", "pressure", "mass", "particle_spacing"] results = Dict{String, Array{Float64}}() @@ -49,11 +51,11 @@ function vtk2trixi(file) all_keys = keys(point_data) idx = findfirst(k -> occursin(field, k), all_keys) if idx !== nothing - results[field] = ReadVTK.get_data(point_data[all_keys[idx]]) + results[field] = convert.(ELTYPE, ReadVTK.get_data(point_data[all_keys[idx]])) else # Use zeros as default values when a field is missing results[field] = field in ["mass"] ? - zeros(size(coordinates, 2)) : zero(coordinates) + zeros(ELTYPE, size(coordinates, 2)) : zero(coordinates) @info "No '$field' field found in VTK file. Will be set to zero." end end @@ -62,7 +64,8 @@ function vtk2trixi(file) first(results["particle_spacing"]) : results["particle_spacing"] - return InitialCondition(; coordinates, particle_spacing=particle_spacing, + return InitialCondition(; coordinates, + particle_spacing=convert(ELTYPE, particle_spacing), velocity=results["velocity"], mass=results["mass"], density=results["density"], From 2b2fb72f724f4437fcdc35b15c0354f60ba9d0d5 Mon Sep 17 00:00:00 2001 From: Marcel Schurer Date: Wed, 3 Dec 2025 08:40:05 +0100 Subject: [PATCH 007/134] Fix formatting in documentation --- src/io/read_vtk.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/io/read_vtk.jl b/src/io/read_vtk.jl index 02e341f1ae..bdf1b7033e 100644 --- a/src/io/read_vtk.jl +++ b/src/io/read_vtk.jl @@ -30,6 +30,7 @@ data = vtk2trixi(joinpath("out", "rectangular.vtu"); # output NamedTuple{data...} +``` """ function vtk2trixi(file; custom_quantities...) vtk_file = ReadVTK.VTKFile(file) From b043bd11e4cc43cebb23bdba1388f5174f59a653 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Mon, 8 Dec 2025 17:52:26 +0100 Subject: [PATCH 008/134] add `coordinates_eltype` --- src/io/read_vtk.jl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/io/read_vtk.jl b/src/io/read_vtk.jl index 326773d4b4..884588033b 100644 --- a/src/io/read_vtk.jl +++ b/src/io/read_vtk.jl @@ -33,8 +33,9 @@ ic = vtk2trixi(joinpath("out", "rectangular.vtu")) └──────────────────────────────────────────────────────────────────────────────────────────────────┘ ``` """ -function vtk2trixi(file; element_type=Float64) +function vtk2trixi(file; element_type=Float64, coordinates_eltype=Float64) ELTYPE = element_type + cELTYPE = coordinates_eltype vtk_file = ReadVTK.VTKFile(file) # Retrieve data fields (e.g., pressure, velocity, ...) @@ -43,7 +44,7 @@ function vtk2trixi(file; element_type=Float64) # Retrieve fields ndims = first(ReadVTK.get_data(field_data["ndims"])) - coordinates = convert.(ELTYPE, ReadVTK.get_points(vtk_file)[1:ndims, :]) + coordinates = convert.(cELTYPE, ReadVTK.get_points(vtk_file)[1:ndims, :]) fields = ["velocity", "density", "pressure", "mass", "particle_spacing"] results = Dict{String, Array{Float64}}() From a9b0f2f9df1d2b9f247ff6053ab27a6d082766ed Mon Sep 17 00:00:00 2001 From: Marcel Schurer Date: Tue, 9 Dec 2025 14:03:28 +0100 Subject: [PATCH 009/134] Corrected failing doctest output --- src/io/read_vtk.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/io/read_vtk.jl b/src/io/read_vtk.jl index bdf1b7033e..4189ea474a 100644 --- a/src/io/read_vtk.jl +++ b/src/io/read_vtk.jl @@ -29,7 +29,7 @@ data = vtk2trixi(joinpath("out", "rectangular.vtu"); my_custom_quantity="my_custom_quantity") # output -NamedTuple{data...} + ``` """ function vtk2trixi(file; custom_quantities...) From 0359425131784b5ebc3064a15fdc03b2613d615f Mon Sep 17 00:00:00 2001 From: LasNikas Date: Wed, 10 Dec 2025 09:30:19 +0100 Subject: [PATCH 010/134] first prototype --- .../boundary/open_boundary/boundary_zones.jl | 23 ++++- src/schemes/boundary/open_boundary/system.jl | 99 ++++++++++++++++--- 2 files changed, 102 insertions(+), 20 deletions(-) diff --git a/src/schemes/boundary/open_boundary/boundary_zones.jl b/src/schemes/boundary/open_boundary/boundary_zones.jl index 7088fea58e..be5979ba6e 100644 --- a/src/schemes/boundary/open_boundary/boundary_zones.jl +++ b/src/schemes/boundary/open_boundary/boundary_zones.jl @@ -147,7 +147,7 @@ bidirectional_flow = BoundaryZone(; boundary_face=face_vertices, face_normal, !!! warning "Experimental Implementation" This is an experimental feature and may change in any future releases. """ -struct BoundaryZone{IC, S, ZO, ZW, FD, FN, ELTYPE, R} +struct BoundaryZone{IC, S, ZO, ZW, FD, FN, ELTYPE, R, C} initial_condition :: IC spanning_set :: S zone_origin :: ZO @@ -156,6 +156,7 @@ struct BoundaryZone{IC, S, ZO, ZW, FD, FN, ELTYPE, R} face_normal :: FN rest_pressure :: ELTYPE # Only required for `BoundaryModelDynamicalPressureZhang` reference_values :: R + cache :: C # Note that the following can't be static type parameters, as all boundary zones in a system # must have the same type, so that we can loop over them in a type-stable way. average_inflow_velocity :: Bool @@ -167,7 +168,7 @@ end function BoundaryZone(; boundary_face, face_normal, density, particle_spacing, initial_condition=nothing, extrude_geometry=nothing, open_boundary_layers::Integer, average_inflow_velocity=true, - boundary_type=BidirectionalFlow(), + boundary_type=BidirectionalFlow(), sample_points=nothing, rest_pressure=zero(eltype(density)), reference_density=nothing, reference_pressure=nothing, reference_velocity=nothing) @@ -262,10 +263,12 @@ function BoundaryZone(; boundary_face, face_normal, density, particle_spacing, ic.velocity .= stack(velocity_ref.(coordinates_svector, 0)) end + cache = (; create_cache_boundary_zone(ic, sample_points)...) + return BoundaryZone(ic, spanning_set_, zone_origin, zone_width, flow_direction, face_normal_, rest_pressure, reference_values, - average_inflow_velocity, prescribed_density, prescribed_pressure, - prescribed_velocity) + cache, average_inflow_velocity, prescribed_density, + prescribed_pressure, prescribed_velocity) end function boundary_type_name(boundary_zone::BoundaryZone) @@ -301,6 +304,16 @@ function Base.show(io::IO, ::MIME"text/plain", boundary_zone::BoundaryZone) end end +create_cache_boundary_zone(initial_condition, sample_points::Nothing) = (;) + +function create_cache_boundary_zone(initial_condition, sample_points::Matrix) + # TODO: Check matrix (ndims etc.) + shepard_coefficient = zeros(eltype(initial_condition), axes(sample_points, 2)) + dA = initial_condition.particle_spacing + return (; sample_points=sample_points, sample_velocity=copy(sample_points), + shepard_coefficient, dA) +end + function set_up_boundary_zone(boundary_face, face_normal, density, particle_spacing, initial_condition, extrude_geometry, open_boundary_layers, boundary_type) @@ -453,7 +466,7 @@ function update_boundary_zone_indices!(system, u, boundary_zones, semi) # - Floating-point rounding when a particle lies almost exactly on the `boundary_face` # during transition, causing a reset just outside the zone # (fixed in https://github.com/trixi-framework/TrixiParticles.jl/pull/997). - @assert system.boundary_zone_indices[particle] != 0 "No boundary zone found for active buffer particle" + @assert system.boundary_zone_indices[particle]!=0 "No boundary zone found for active buffer particle" end return system diff --git a/src/schemes/boundary/open_boundary/system.jl b/src/schemes/boundary/open_boundary/system.jl index 9396c3f1fb..2c4c5c6c0c 100644 --- a/src/schemes/boundary/open_boundary/system.jl +++ b/src/schemes/boundary/open_boundary/system.jl @@ -63,7 +63,7 @@ end function OpenBoundarySystem(boundary_zones::Union{BoundaryZone, Nothing}...; fluid_system::AbstractFluidSystem, buffer_size::Integer, - boundary_model, + boundary_model, calculate_flow_rate=false, pressure_acceleration=boundary_model isa BoundaryModelDynamicalPressureZhang ? fluid_system.pressure_acceleration_formulation : @@ -85,7 +85,7 @@ function OpenBoundarySystem(boundary_zones::Union{BoundaryZone, Nothing}...; cache = (; create_cache_shifting(initial_conditions, shifting_technique)..., create_cache_open_boundary(boundary_model, fluid_system, initial_conditions, - boundary_zones_)...) + calculate_flow_rate, boundary_zones_)...) fluid_system_index = Ref(0) @@ -110,6 +110,7 @@ function OpenBoundarySystem(boundary_zones::Union{BoundaryZone, Nothing}...; zone.face_normal, zone.rest_pressure, nothing, + zone.cache, zone.average_inflow_velocity, zone.prescribed_density, zone.prescribed_pressure, @@ -132,7 +133,7 @@ function initialize!(system::OpenBoundarySystem, semi) end function create_cache_open_boundary(boundary_model, fluid_system, initial_condition, - boundary_zones) + calculate_flow_rate, boundary_zones) reference_values = map(bz -> bz.reference_values, boundary_zones) ELTYPE = eltype(initial_condition) @@ -141,6 +142,15 @@ function create_cache_open_boundary(boundary_model, fluid_system, initial_condit density_reference_values = map(ref -> ref.reference_density, reference_values) velocity_reference_values = map(ref -> ref.reference_velocity, reference_values) + cache = (; pressure_reference_values=pressure_reference_values, + density_reference_values=density_reference_values, + velocity_reference_values=velocity_reference_values, calculate_flow_rate) + + if calculate_flow_rate + boundary_zones_flow_rate = zeros(ELTYPE, length(boundary_zones)) + cache = (; boundary_zones_flow_rate, cache...) + end + if boundary_model isa BoundaryModelCharacteristicsLastiwka characteristics = zeros(ELTYPE, 3, nparticles(initial_condition)) previous_characteristics = zeros(ELTYPE, 3, nparticles(initial_condition)) @@ -148,10 +158,8 @@ function create_cache_open_boundary(boundary_model, fluid_system, initial_condit return (; characteristics=characteristics, previous_characteristics=previous_characteristics, pressure=copy(initial_condition.pressure), - density=copy(initial_condition.density), - pressure_reference_values=pressure_reference_values, - density_reference_values=density_reference_values, - velocity_reference_values=velocity_reference_values) + density=copy(initial_condition.density), cache...) + elseif boundary_model isa BoundaryModelDynamicalPressureZhang # A separate array for the boundary pressure is required, # since it is specified independently from the computed pressure for the momentum equation. @@ -171,10 +179,7 @@ function create_cache_open_boundary(boundary_model, fluid_system, initial_condit cache = (; density_calculator=ContinuityDensity(), density_diffusion=density_diffusion_, pressure_boundary=pressure_boundary, - density_rest=density_rest, - pressure_reference_values=pressure_reference_values, - density_reference_values=density_reference_values, - velocity_reference_values=velocity_reference_values) + density_rest=density_rest, cache...) if fluid_system isa EntropicallyDampedSPHSystem # Density and pressure is stored in `v` @@ -186,10 +191,7 @@ function create_cache_open_boundary(boundary_model, fluid_system, initial_condit else return (; pressure=copy(initial_condition.pressure), - density=copy(initial_condition.density), - pressure_reference_values=pressure_reference_values, - density_reference_values=density_reference_values, - velocity_reference_values=velocity_reference_values) + density=copy(initial_condition.density), cache...) end end @@ -305,6 +307,10 @@ function update_open_boundary_eachstep!(system::OpenBoundarySystem, v_ode, u_ode u = wrap_u(u_ode, system, semi) v = wrap_v(v_ode, system, semi) + # This must be called before `update_pressure_model!` and `check_domain!` + # to ensure quantities remain consistent with the current simulation state. + calculate_flow_rate!(system, v, u, v_ode, u_ode, semi) + @trixi_timeit timer() "check domain" check_domain!(system, v, u, v_ode, u_ode, semi) update_pressure_model!(system, v, u, semi, integrator.dt) @@ -323,6 +329,28 @@ end update_open_boundary_eachstep!(system, v_ode, u_ode, semi, t, integrator) = system +function calculate_flow_rate!(system, v, u, v_ode, u_ode, semi) + system.cache.calculate_flow_rate || return system + + for boundary_zone in system.boundary_zones + interpolate_velocity!(system, boundary_zone, v, u, v_ode, u_ode, semi) + end + + foreach_enumerate(system.boundary_zones) do (zone_id, boundary_zone) + (; face_normal) = boundary_zone + (; sample_velocity, dA) = boundary_zone.cache + # Compute volumetric flow rate: Q = ∫ v ⋅ n dA + current_flow_rate = sum(axes(sample_velocity, 2)) do point + vn = dot(current_velocity(sample_velocity, system, point), -face_normal) + return vn * dA + end + + system.cache.boundary_zones_flow_rate[zone_id] = current_flow_rate + end + + return system +end + function check_domain!(system, v, u, v_ode, u_ode, semi) (; boundary_zones, boundary_candidates, fluid_candidates, fluid_system) = system @@ -570,3 +598,44 @@ end return system end + +function interpolate_velocity!(system::OpenBoundarySystem, boundary_zone, v, u, + v_ode, u_ode, semi) + (; sample_points, sample_velocity, shepard_coefficient) = boundary_zone.cache + smoothing_length = initial_smoothing_length(system) + smoothing_kernel = system_smoothing_kernel(system) + + set_zero!(shepard_coefficient) + set_zero!(sample_velocity) + + foreach_system(semi) do neighbor_system + v_neighbor = wrap_v(v_ode, neighbor_system, semi) + u_neighbor = wrap_u(u_ode, neighbor_system, semi) + neighbor_coords = current_coordinates(u_neighbor, neighbor_system) + + foreach_point_neighbor(system, neighbor_system, sample_points, neighbor_coords, + semi, + points=axes(sample_points, 2)) do point, neighbor, + pos_diff, distance + m_b = hydrodynamic_mass(neighbor_system, neighbor) + volume_b = m_b / current_density(v_neighbor, neighbor_system, neighbor) + W_ab = kernel(smoothing_kernel, distance, smoothing_length) + shepard_coefficient[point] += volume_b * W_ab + + velocity_neighbor = viscous_velocity(v_neighbor, neighbor_system, neighbor) + for i in axes(velocity_neighbor, 1) + sample_velocity[i, point] += velocity_neighbor[i] * volume_b * W_ab + end + end + end + + @threaded semi for point in axes(sample_points, 2) + if shepard_coefficient[point] > eps() + for i in axes(sample_velocity, 1) + sample_velocity[i, point] /= shepard_coefficient[point] + end + end + end + + return system +end From fd15671fcb56a9a37a276d75a396457b10b94cd5 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Wed, 10 Dec 2025 11:03:16 +0100 Subject: [PATCH 011/134] write Q --- src/io/write_vtk.jl | 8 ++++++++ src/schemes/boundary/open_boundary/boundary_zones.jl | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/io/write_vtk.jl b/src/io/write_vtk.jl index 07fa559d2e..d1146e67b1 100644 --- a/src/io/write_vtk.jl +++ b/src/io/write_vtk.jl @@ -409,6 +409,14 @@ function write2vtk!(vtk, v, u, t, system::OpenBoundarySystem) vtk["pressure"] = [current_pressure(v, system, particle) for particle in eachparticle(system)] + if system.cache.calculate_flow_rate + for i in eachindex(system.cache.boundary_zones_flow_rate) + vtk["Q_$i"] = system.cache.boundary_zones_flow_rate[i] + end + + vtk["Q_total"] = sum(system.cache.boundary_zones_flow_rate) + end + return vtk end diff --git a/src/schemes/boundary/open_boundary/boundary_zones.jl b/src/schemes/boundary/open_boundary/boundary_zones.jl index be5979ba6e..2bde137093 100644 --- a/src/schemes/boundary/open_boundary/boundary_zones.jl +++ b/src/schemes/boundary/open_boundary/boundary_zones.jl @@ -466,7 +466,7 @@ function update_boundary_zone_indices!(system, u, boundary_zones, semi) # - Floating-point rounding when a particle lies almost exactly on the `boundary_face` # during transition, causing a reset just outside the zone # (fixed in https://github.com/trixi-framework/TrixiParticles.jl/pull/997). - @assert system.boundary_zone_indices[particle]!=0 "No boundary zone found for active buffer particle" + @assert system.boundary_zone_indices[particle] != 0 "No boundary zone found for active buffer particle" end return system From 364e24ea6894836aee569d2e11d7a934c0a7642b Mon Sep 17 00:00:00 2001 From: LasNikas Date: Wed, 10 Dec 2025 11:42:06 +0100 Subject: [PATCH 012/134] gpu fix --- src/io/write_vtk.jl | 6 ++++-- src/schemes/boundary/open_boundary/system.jl | 20 +++++++++++++------- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/io/write_vtk.jl b/src/io/write_vtk.jl index d1146e67b1..ff2f2f85eb 100644 --- a/src/io/write_vtk.jl +++ b/src/io/write_vtk.jl @@ -410,11 +410,13 @@ function write2vtk!(vtk, v, u, t, system::OpenBoundarySystem) for particle in eachparticle(system)] if system.cache.calculate_flow_rate + Q_total = zero(eltype(system)) for i in eachindex(system.cache.boundary_zones_flow_rate) - vtk["Q_$i"] = system.cache.boundary_zones_flow_rate[i] + vtk["Q_$i"] = system.cache.boundary_zones_flow_rate[i][] + Q_total += system.cache.boundary_zones_flow_rate[i][] end - vtk["Q_total"] = sum(system.cache.boundary_zones_flow_rate) + vtk["Q_total"] = Q_total end return vtk diff --git a/src/schemes/boundary/open_boundary/system.jl b/src/schemes/boundary/open_boundary/system.jl index 2c4c5c6c0c..12cde7de5e 100644 --- a/src/schemes/boundary/open_boundary/system.jl +++ b/src/schemes/boundary/open_boundary/system.jl @@ -147,7 +147,8 @@ function create_cache_open_boundary(boundary_model, fluid_system, initial_condit velocity_reference_values=velocity_reference_values, calculate_flow_rate) if calculate_flow_rate - boundary_zones_flow_rate = zeros(ELTYPE, length(boundary_zones)) + boundary_zones_flow_rate = ntuple(i -> Ref(zero(ELTYPE)), + Val(length(boundary_zones))) cache = (; boundary_zones_flow_rate, cache...) end @@ -329,23 +330,28 @@ end update_open_boundary_eachstep!(system, v_ode, u_ode, semi, t, integrator) = system -function calculate_flow_rate!(system, v, u, v_ode, u_ode, semi) - system.cache.calculate_flow_rate || return system +function calculate_flow_rate!(system::OpenBoundarySystem{<:Any, ELTYPE, NDIMS}, v, u, v_ode, + u_ode, semi) where {ELTYPE, NDIMS} + (; calculate_flow_rate, boundary_zones_flow_rate) = system.cache + calculate_flow_rate || return system for boundary_zone in system.boundary_zones interpolate_velocity!(system, boundary_zone, v, u, v_ode, u_ode, semi) end - foreach_enumerate(system.boundary_zones) do (zone_id, boundary_zone) + foreach_enumerate(boundary_zones_flow_rate) do (zone_id, boundary_zone_flow_rate) + boundary_zone = system.boundary_zones[zone_id] (; face_normal) = boundary_zone (; sample_velocity, dA) = boundary_zone.cache + # Compute volumetric flow rate: Q = ∫ v ⋅ n dA - current_flow_rate = sum(axes(sample_velocity, 2)) do point - vn = dot(current_velocity(sample_velocity, system, point), -face_normal) + velocities = reinterpret(reshape, SVector{NDIMS, ELTYPE}, sample_velocity) + current_flow_rate = sum(velocities) do velocity + vn = dot(velocity, -face_normal) return vn * dA end - system.cache.boundary_zones_flow_rate[zone_id] = current_flow_rate + boundary_zone_flow_rate[] = current_flow_rate end return system From c1b0abf6536801fa6669908a5f32030b31d693f5 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Wed, 10 Dec 2025 14:46:11 +0100 Subject: [PATCH 013/134] add checks --- .../boundary/open_boundary/boundary_zones.jl | 54 ++++++++++++++++--- src/schemes/boundary/open_boundary/system.jl | 14 +++-- 2 files changed, 56 insertions(+), 12 deletions(-) diff --git a/src/schemes/boundary/open_boundary/boundary_zones.jl b/src/schemes/boundary/open_boundary/boundary_zones.jl index 2bde137093..bfefefca4c 100644 --- a/src/schemes/boundary/open_boundary/boundary_zones.jl +++ b/src/schemes/boundary/open_boundary/boundary_zones.jl @@ -263,7 +263,8 @@ function BoundaryZone(; boundary_face, face_normal, density, particle_spacing, ic.velocity .= stack(velocity_ref.(coordinates_svector, 0)) end - cache = (; create_cache_boundary_zone(ic, sample_points)...) + cache = (; + create_cache_boundary_zone(ic, boundary_face, face_normal_, sample_points)...) return BoundaryZone(ic, spanning_set_, zone_origin, zone_width, flow_direction, face_normal_, rest_pressure, reference_values, @@ -300,18 +301,55 @@ function Base.show(io::IO, ::MIME"text/plain", boundary_zone::BoundaryZone) summary_line(io, "boundary type", boundary_type_name(boundary_zone)) summary_line(io, "#particles", nparticles(boundary_zone.initial_condition)) summary_line(io, "width", round(boundary_zone.zone_width, digits=6)) + if hasproperty(boundary_zone.cache, :cross_sectional_area) + summary_line(io, "cross sectional area", + boundary_zone.cache.cross_sectional_area) + end summary_footer(io) end end -create_cache_boundary_zone(initial_condition, sample_points::Nothing) = (;) +function create_cache_boundary_zone(initial_condition, boundary_face, face_normal, + sample_points::Nothing) + return (; sample_points) +end + +function create_cache_boundary_zone(initial_condition, boundary_face, face_normal, + sample_points) + (; particle_spacing) = initial_condition + area_increment = particle_spacing^(ndims(initial_condition) - 1) + if sample_points === :default + sample_points_ = extrude_geometry(boundary_face; particle_spacing, density=Inf, + direction=(-face_normal), n_extrude=1).coordinates + else + if !(sample_points isa Matrix && size(sample_points, 1) == ndims(initial_condition)) + throw(ArgumentError("`sample_points` must be a matrix with " * + "`ndims(initial_condition)` rows")) + end + + # TODO: Check if regular grid? + + sample_points_ = sample_points + end + + discrete_face_area = area_increment * size(sample_points_, 2) + + if ndims(initial_condition) == 3 + v1, v2, v3 = boundary_face + face_area = norm(cross(v2 - v1, v3 - v1)) + elseif ndims(initial_condition) == 2 + v1, v2 = boundary_face + face_area = norm(v2 - v1) + end + + if discrete_face_area > face_area + @warn "The sampled area of the boundary face " * + "($(discrete_face_area)) is larger than the actual face area ($(face_area)). " + end -function create_cache_boundary_zone(initial_condition, sample_points::Matrix) - # TODO: Check matrix (ndims etc.) - shepard_coefficient = zeros(eltype(initial_condition), axes(sample_points, 2)) - dA = initial_condition.particle_spacing - return (; sample_points=sample_points, sample_velocity=copy(sample_points), - shepard_coefficient, dA) + return (; sample_points=sample_points_, sample_velocity=copy(sample_points_), + shepard_coefficient=zeros(eltype(initial_condition), size(sample_points_, 2)), + area_increment, cross_sectional_area=discrete_face_area) end function set_up_boundary_zone(boundary_face, face_normal, density, particle_spacing, diff --git a/src/schemes/boundary/open_boundary/system.jl b/src/schemes/boundary/open_boundary/system.jl index 12cde7de5e..4bed038600 100644 --- a/src/schemes/boundary/open_boundary/system.jl +++ b/src/schemes/boundary/open_boundary/system.jl @@ -147,6 +147,12 @@ function create_cache_open_boundary(boundary_model, fluid_system, initial_condit velocity_reference_values=velocity_reference_values, calculate_flow_rate) if calculate_flow_rate + if any(zone -> isnothing(zone.cache.sample_points), boundary_zones) + throw(ArgumentError("`sample_points` must be specified for all boundary zones when " * + "`calculate_flow_rate` is true.\n" * + "Use `sample_points=:default` to automatically generate sample points.")) + end + boundary_zones_flow_rate = ntuple(i -> Ref(zero(ELTYPE)), Val(length(boundary_zones))) cache = (; boundary_zones_flow_rate, cache...) @@ -332,8 +338,8 @@ update_open_boundary_eachstep!(system, v_ode, u_ode, semi, t, integrator) = syst function calculate_flow_rate!(system::OpenBoundarySystem{<:Any, ELTYPE, NDIMS}, v, u, v_ode, u_ode, semi) where {ELTYPE, NDIMS} - (; calculate_flow_rate, boundary_zones_flow_rate) = system.cache - calculate_flow_rate || return system + system.cache.calculate_flow_rate || return system + (; boundary_zones_flow_rate) = system.cache for boundary_zone in system.boundary_zones interpolate_velocity!(system, boundary_zone, v, u, v_ode, u_ode, semi) @@ -342,13 +348,13 @@ function calculate_flow_rate!(system::OpenBoundarySystem{<:Any, ELTYPE, NDIMS}, foreach_enumerate(boundary_zones_flow_rate) do (zone_id, boundary_zone_flow_rate) boundary_zone = system.boundary_zones[zone_id] (; face_normal) = boundary_zone - (; sample_velocity, dA) = boundary_zone.cache + (; sample_velocity, area_increment) = boundary_zone.cache # Compute volumetric flow rate: Q = ∫ v ⋅ n dA velocities = reinterpret(reshape, SVector{NDIMS, ELTYPE}, sample_velocity) current_flow_rate = sum(velocities) do velocity vn = dot(velocity, -face_normal) - return vn * dA + return vn * area_increment end boundary_zone_flow_rate[] = current_flow_rate From eeebcc22a371b01d4d51d50ddf90ad31c3ae7a40 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Wed, 10 Dec 2025 15:14:33 +0100 Subject: [PATCH 014/134] add docs --- src/schemes/boundary/open_boundary/boundary_zones.jl | 6 +++++- src/schemes/boundary/open_boundary/system.jl | 4 +++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/schemes/boundary/open_boundary/boundary_zones.jl b/src/schemes/boundary/open_boundary/boundary_zones.jl index bfefefca4c..043beb8196 100644 --- a/src/schemes/boundary/open_boundary/boundary_zones.jl +++ b/src/schemes/boundary/open_boundary/boundary_zones.jl @@ -81,6 +81,10 @@ There are three ways to specify the actual shape of the boundary zone: Per default it is set to zero (assuming a gauge pressure system). - For `EntropicallyDampedSPHSystem`: Use the initial pressure from the `InitialCondition` - For `WeaklyCompressibleSPHSystem`: Use the background pressure from the equation of state +- `sample_points=:default`: Either `:default` to automatically generate sample points on the boundary face (default), + or a matrix of dimensions `(ndims, n_points)` containing sample points + on the boundary face used to compute the volumetric flow rate. + Set to `nothing` to skip sampling. !!! note "Note" The reference values (`reference_velocity`, `reference_pressure`, `reference_density`) @@ -168,7 +172,7 @@ end function BoundaryZone(; boundary_face, face_normal, density, particle_spacing, initial_condition=nothing, extrude_geometry=nothing, open_boundary_layers::Integer, average_inflow_velocity=true, - boundary_type=BidirectionalFlow(), sample_points=nothing, + boundary_type=BidirectionalFlow(), sample_points=:default, rest_pressure=zero(eltype(density)), reference_density=nothing, reference_pressure=nothing, reference_velocity=nothing) diff --git a/src/schemes/boundary/open_boundary/system.jl b/src/schemes/boundary/open_boundary/system.jl index 4bed038600..36889bc0e2 100644 --- a/src/schemes/boundary/open_boundary/system.jl +++ b/src/schemes/boundary/open_boundary/system.jl @@ -1,7 +1,7 @@ @doc raw""" OpenBoundarySystem(boundary_zone::BoundaryZone; fluid_system::AbstractFluidSystem, buffer_size::Integer, - boundary_model) + boundary_model, calculate_flow_rate=false) Open boundary system for in- and outflow particles. @@ -11,6 +11,8 @@ Open boundary system for in- and outflow particles. # Keywords - `fluid_system`: The corresponding fluid system - `boundary_model`: Boundary model (see [Open Boundary Models](@ref open_boundary_models)) +- `calculate_flow_rate=false`: Set to `true` to calculate the volumetric flow rate through each boundary zone. + Default is `false`. - `buffer_size`: Number of buffer particles. - `pressure_acceleration`: Pressure acceleration formulation for the system. Required only when using [`BoundaryModelDynamicalPressureZhang`](@ref). From d8b5b0091aba86fabd02d5b014d697674cbdcc7b Mon Sep 17 00:00:00 2001 From: Marcel Schurer Date: Wed, 10 Dec 2025 15:50:22 +0100 Subject: [PATCH 015/134] Corrected failing doctest output --- src/io/read_vtk.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/io/read_vtk.jl b/src/io/read_vtk.jl index 4189ea474a..f98d35d10c 100644 --- a/src/io/read_vtk.jl +++ b/src/io/read_vtk.jl @@ -15,7 +15,7 @@ Load VTK file and convert data to a NamedTuple. This is an experimental feature and may change in any future releases. # Example -```jldoctest; output = false +```jldoctest; output = false, filter = r"density = \\[.*\\]|pressure = \\[.*\\]|mass = \\[.*\\]|velocity = \\[.*\\]|coordinates = \\[.*\\]" # Create a rectangular shape rectangular = RectangularShape(0.1, (10, 10), (0, 0), density=1.5, velocity=(1.0, -2.0), pressure=1000.0) @@ -29,7 +29,7 @@ data = vtk2trixi(joinpath("out", "rectangular.vtu"); my_custom_quantity="my_custom_quantity") # output - +(particle_spacing = 0.1, density = [...], time = 0.0, pressure = [...], mass = [...], my_custom_quantity = 3.0, velocity = [...], coordinates = [...]) ``` """ function vtk2trixi(file; custom_quantities...) From d21a5492311db0c13e75aa7fa77866cef49814d5 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Wed, 10 Dec 2025 17:29:05 +0100 Subject: [PATCH 016/134] adapt pressure model --- .../boundary/open_boundary/pressure_model.jl | 39 +++++-------------- src/schemes/boundary/open_boundary/system.jl | 4 ++ src/util.jl | 14 +++++++ .../boundary/open_boundary/pressure_model.jl | 6 +-- 4 files changed, 30 insertions(+), 33 deletions(-) diff --git a/src/schemes/boundary/open_boundary/pressure_model.jl b/src/schemes/boundary/open_boundary/pressure_model.jl index 81ce8f9ff4..7e180396a1 100644 --- a/src/schemes/boundary/open_boundary/pressure_model.jl +++ b/src/schemes/boundary/open_boundary/pressure_model.jl @@ -67,51 +67,32 @@ function update_pressure_model!(system, v, u, semi, dt) if any(pm -> isa(pm, AbstractPressureModel), system.cache.pressure_reference_values) @trixi_timeit timer() "update pressure model" begin - calculate_flow_rate_and_pressure!(system, v, u, dt) + calculate_pressure!(system, dt) end end return system end -function calculate_flow_rate_and_pressure!(system, v, u, dt) - (; pressure_reference_values) = system.cache - foreach_enumerate(pressure_reference_values) do (zone_id, pressure_model) - boundary_zone = system.boundary_zones[zone_id] - calculate_flow_rate_and_pressure!(pressure_model, system, boundary_zone, v, u, dt) +function calculate_pressure!(system, dt) + (; pressure_reference_values, boundary_zones_flow_rate) = system.cache + + foreach_noalloc(pressure_reference_values, + boundary_zones_flow_rate) do (pressure_model, flow_rate) + calculate_pressure!(pressure_model, system, flow_rate[], dt) end return system end -function calculate_flow_rate_and_pressure!(pressure_model, system, boundary_zone, v, u, dt) +function calculate_pressure!(pressure_model, system, current_flow_rate, dt) return pressure_model end -function calculate_flow_rate_and_pressure!(pressure_model::RCRWindkesselModel, system, - boundary_zone, v, u, dt) - (; particle_spacing) = system.initial_condition +function calculate_pressure!(pressure_model::RCRWindkesselModel, system, + current_flow_rate, dt) (; characteristic_resistance, peripheral_resistance, compliance, flow_rate, pressure) = pressure_model - (; face_normal) = boundary_zone - - # Find particles within the current boundary zone - candidates = findall(particle -> boundary_zone == - current_boundary_zone(system, particle), - each_integrated_particle(system)) - - # Assuming negligible transverse velocity gradients within the boundary zone, - # the full area of the zone is taken as the representative cross-sectional - # area for volumetric flow-rate estimation. - cross_sectional_area = length(candidates) * particle_spacing^(ndims(system) - 1) - - # Division inside the `sum` closure to maintain GPU compatibility - velocity_avg = sum(candidates) do particle - return dot(current_velocity(v, system, particle), -face_normal) / length(candidates) - end - - # Compute volumetric flow rate: Q = v * A - current_flow_rate = velocity_avg * cross_sectional_area previous_pressure = pressure[] previous_flow_rate = flow_rate[] diff --git a/src/schemes/boundary/open_boundary/system.jl b/src/schemes/boundary/open_boundary/system.jl index 36889bc0e2..7038f48534 100644 --- a/src/schemes/boundary/open_boundary/system.jl +++ b/src/schemes/boundary/open_boundary/system.jl @@ -144,6 +144,10 @@ function create_cache_open_boundary(boundary_model, fluid_system, initial_condit density_reference_values = map(ref -> ref.reference_density, reference_values) velocity_reference_values = map(ref -> ref.reference_velocity, reference_values) + if any(pr -> isa(pr, AbstractPressureModel), pressure_reference_values) + calculate_flow_rate = true + end + cache = (; pressure_reference_values=pressure_reference_values, density_reference_values=density_reference_values, velocity_reference_values=velocity_reference_values, calculate_flow_rate) diff --git a/src/util.jl b/src/util.jl index 18389cd44b..be032a8b95 100644 --- a/src/util.jl +++ b/src/util.jl @@ -11,6 +11,20 @@ end @inline foreach_noalloc(func, collection::Tuple{}) = nothing +@inline function foreach_noalloc(func, collection1, collection2) + element1 = first(collection1) + remaining_collection1 = Base.tail(collection1) + element2 = first(collection2) + remaining_collection2 = Base.tail(collection2) + + func((element1, element2)) + + # Process remaining collection + foreach_noalloc(func, remaining_collection1, remaining_collection2) +end + +@inline foreach_noalloc(func, collection1::Tuple{}, collection2::Tuple{}) = nothing + # Same as `foreach(enumerate(something))`, but without allocations. # Note that compile times may increase if this is used with big tuples. @inline foreach_enumerate(func, collection) = foreach_enumerate(func, collection, 1) diff --git a/test/schemes/boundary/open_boundary/pressure_model.jl b/test/schemes/boundary/open_boundary/pressure_model.jl index f35c2e867f..c0e1baffe7 100644 --- a/test/schemes/boundary/open_boundary/pressure_model.jl +++ b/test/schemes/boundary/open_boundary/pressure_model.jl @@ -81,15 +81,13 @@ boundary_model=nothing, fluid_system=FluidSystemMockRCR(nothing, nothing)) system.boundary_zone_indices .= 1 - - u = system.initial_condition.coordinates v = system.initial_condition.velocity times = collect(tspan[1]:dt:tspan[2]) p_calculated = empty(times) for t in times - v[1, :] .= func(t) - TrixiParticles.calculate_flow_rate_and_pressure!(system, v, u, dt) + system.cache.boundary_zones_flow_rate[1][] = func(t) + TrixiParticles.calculate_pressure!(system, dt) # Store only values after the seventh cycle if t >= 7T From b4020c8df060d51bb3f012ef4c977aabe14c7834 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Thu, 11 Dec 2025 08:07:32 +0100 Subject: [PATCH 017/134] use `foreach_no_alloc` --- examples/fluid/poiseuille_flow_2d.jl | 3 +- src/schemes/boundary/open_boundary/system.jl | 40 ++++++++++---------- src/util.jl | 14 +++++++ 3 files changed, 37 insertions(+), 20 deletions(-) diff --git a/examples/fluid/poiseuille_flow_2d.jl b/examples/fluid/poiseuille_flow_2d.jl index dd324a5bca..1afacd81a8 100644 --- a/examples/fluid/poiseuille_flow_2d.jl +++ b/examples/fluid/poiseuille_flow_2d.jl @@ -29,7 +29,7 @@ open_boundary_layers = 10 # ========================================================================================== # ==== Experiment Setup -tspan = (0.0, 2.0) +tspan = (0.0, 1.0) wcsph = true domain_size = (flow_length, wall_distance) @@ -136,6 +136,7 @@ outflow = BoundaryZone(; boundary_face=face_out, face_normal=(.-(flow_direction) open_boundary = OpenBoundarySystem(inflow, outflow; fluid_system, boundary_model=open_boundary_model, + calculate_flow_rate=true, buffer_size=n_buffer_particles) # ========================================================================================== diff --git a/src/schemes/boundary/open_boundary/system.jl b/src/schemes/boundary/open_boundary/system.jl index 36889bc0e2..648a97cb03 100644 --- a/src/schemes/boundary/open_boundary/system.jl +++ b/src/schemes/boundary/open_boundary/system.jl @@ -318,7 +318,7 @@ function update_open_boundary_eachstep!(system::OpenBoundarySystem, v_ode, u_ode # This must be called before `update_pressure_model!` and `check_domain!` # to ensure quantities remain consistent with the current simulation state. - calculate_flow_rate!(system, v, u, v_ode, u_ode, semi) + calculate_flow_rate!(system, v_ode, u_ode, semi) @trixi_timeit timer() "check domain" check_domain!(system, v, u, v_ode, u_ode, semi) @@ -338,28 +338,30 @@ end update_open_boundary_eachstep!(system, v_ode, u_ode, semi, t, integrator) = system -function calculate_flow_rate!(system::OpenBoundarySystem{<:Any, ELTYPE, NDIMS}, v, u, v_ode, - u_ode, semi) where {ELTYPE, NDIMS} +function calculate_flow_rate!(system::OpenBoundarySystem{<:Any, ELTYPE, NDIMS}, + v_ode, u_ode, semi) where {ELTYPE, NDIMS} system.cache.calculate_flow_rate || return system - (; boundary_zones_flow_rate) = system.cache - for boundary_zone in system.boundary_zones - interpolate_velocity!(system, boundary_zone, v, u, v_ode, u_ode, semi) - end + @trixi_timeit timer() "flow rate calculation" begin + (; boundary_zones) = system + (; boundary_zones_flow_rate) = system.cache - foreach_enumerate(boundary_zones_flow_rate) do (zone_id, boundary_zone_flow_rate) - boundary_zone = system.boundary_zones[zone_id] - (; face_normal) = boundary_zone - (; sample_velocity, area_increment) = boundary_zone.cache + foreach_noalloc(boundary_zones, + boundary_zones_flow_rate) do (boundary_zone, flow_rate) + (; face_normal) = boundary_zone + (; sample_velocity, area_increment) = boundary_zone.cache - # Compute volumetric flow rate: Q = ∫ v ⋅ n dA - velocities = reinterpret(reshape, SVector{NDIMS, ELTYPE}, sample_velocity) - current_flow_rate = sum(velocities) do velocity - vn = dot(velocity, -face_normal) - return vn * area_increment - end + interpolate_velocity!(system, boundary_zone, v_ode, u_ode, semi) - boundary_zone_flow_rate[] = current_flow_rate + # Compute volumetric flow rate: Q = ∫ v ⋅ n dA + velocities = reinterpret(reshape, SVector{NDIMS, ELTYPE}, sample_velocity) + current_flow_rate = sum(velocities) do velocity + vn = dot(velocity, -face_normal) + return vn * area_increment + end + + flow_rate[] = current_flow_rate + end end return system @@ -613,7 +615,7 @@ end return system end -function interpolate_velocity!(system::OpenBoundarySystem, boundary_zone, v, u, +function interpolate_velocity!(system::OpenBoundarySystem, boundary_zone, v_ode, u_ode, semi) (; sample_points, sample_velocity, shepard_coefficient) = boundary_zone.cache smoothing_length = initial_smoothing_length(system) diff --git a/src/util.jl b/src/util.jl index 18389cd44b..be032a8b95 100644 --- a/src/util.jl +++ b/src/util.jl @@ -11,6 +11,20 @@ end @inline foreach_noalloc(func, collection::Tuple{}) = nothing +@inline function foreach_noalloc(func, collection1, collection2) + element1 = first(collection1) + remaining_collection1 = Base.tail(collection1) + element2 = first(collection2) + remaining_collection2 = Base.tail(collection2) + + func((element1, element2)) + + # Process remaining collection + foreach_noalloc(func, remaining_collection1, remaining_collection2) +end + +@inline foreach_noalloc(func, collection1::Tuple{}, collection2::Tuple{}) = nothing + # Same as `foreach(enumerate(something))`, but without allocations. # Note that compile times may increase if this is used with big tuples. @inline foreach_enumerate(func, collection) = foreach_enumerate(func, collection, 1) From 55be6a601973cc7a9eff94cafe9d6b0a5114feaf Mon Sep 17 00:00:00 2001 From: LasNikas Date: Thu, 11 Dec 2025 08:28:07 +0100 Subject: [PATCH 018/134] fix unit tests --- .../boundary/open_boundary/boundary_zones.jl | 2 +- .../boundary/open_boundary/boundary_zone.jl | 62 ++++++++++++++----- 2 files changed, 49 insertions(+), 15 deletions(-) diff --git a/src/schemes/boundary/open_boundary/boundary_zones.jl b/src/schemes/boundary/open_boundary/boundary_zones.jl index 043beb8196..55b86be737 100644 --- a/src/schemes/boundary/open_boundary/boundary_zones.jl +++ b/src/schemes/boundary/open_boundary/boundary_zones.jl @@ -346,7 +346,7 @@ function create_cache_boundary_zone(initial_condition, boundary_face, face_norma face_area = norm(v2 - v1) end - if discrete_face_area > face_area + if discrete_face_area > (face_area + eps(face_area)) @warn "The sampled area of the boundary face " * "($(discrete_face_area)) is larger than the actual face area ($(face_area)). " end diff --git a/test/schemes/boundary/open_boundary/boundary_zone.jl b/test/schemes/boundary/open_boundary/boundary_zone.jl index 619c048bbe..3e1fa13118 100644 --- a/test/schemes/boundary/open_boundary/boundary_zone.jl +++ b/test/schemes/boundary/open_boundary/boundary_zone.jl @@ -1,7 +1,7 @@ @testset verbose=true "Boundary Zone" begin @testset "`show`" begin inflow = BoundaryZone(; boundary_face=([0.0, 0.0], [0.0, 1.0]), - particle_spacing=0.05, + particle_spacing=0.05, sample_points=nothing, face_normal=(1.0, 0.0), density=1.0, reference_density=0.0, reference_pressure=0.0, @@ -22,7 +22,7 @@ @test repr("text/plain", inflow) == show_box outflow = BoundaryZone(; boundary_face=([0.0, 0.0], [0.0, 1.0]), - particle_spacing=0.05, + particle_spacing=0.05, sample_points=nothing, reference_density=0.0, reference_pressure=0.0, reference_velocity=[0.0, 0.0], @@ -41,6 +41,48 @@ └──────────────────────────────────────────────────────────────────────────────────────────────────┘""" @test repr("text/plain", outflow) == show_box + + bidirectional = BoundaryZone(; boundary_face=([0.0, 0.0], [0.0, 1.0]), + particle_spacing=0.05, sample_points=nothing, + reference_density=0.0, + reference_pressure=0.0, + reference_velocity=[0.0, 0.0], + face_normal=(1.0, 0.0), density=1.0, + open_boundary_layers=4) + + show_compact = "BoundaryZone() with 80 particles" + @test repr(bidirectional) == show_compact + show_box = """ + ┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ BoundaryZone │ + │ ════════════ │ + │ boundary type: ………………………………………… bidirectional_flow │ + │ #particles: ………………………………………………… 80 │ + │ width: ……………………………………………………………… 0.2 │ + └──────────────────────────────────────────────────────────────────────────────────────────────────┘""" + + @test repr("text/plain", bidirectional) == show_box + + zone = BoundaryZone(; boundary_face=([0.0, 0.0], [1 / sqrt(2), 1 / sqrt(2)]), + particle_spacing=0.05, reference_density=0.0, + reference_pressure=0.0, + reference_velocity=[0.0, 0.0], + face_normal=normalize([-1.0, 1.0]), density=1.0, + open_boundary_layers=4) + + show_compact = "BoundaryZone() with 80 particles" + @test repr(zone) == show_compact + show_box = """ + ┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ BoundaryZone │ + │ ════════════ │ + │ boundary type: ………………………………………… bidirectional_flow │ + │ #particles: ………………………………………………… 80 │ + │ width: ……………………………………………………………… 0.2 │ + │ cross sectional area: ……………………… 1.0 │ + └──────────────────────────────────────────────────────────────────────────────────────────────────┘""" + + @test repr("text/plain", zone) == show_box end @testset verbose=true "Illegal Inputs" begin @@ -127,9 +169,7 @@ outflow ] - @testset verbose=true "$(TrixiParticles.boundary_type_name(boundary_zone))" for boundary_zone in - boundary_zones - + @testset verbose=true "$(TrixiParticles.boundary_type_name(boundary_zone))" for boundary_zone in boundary_zones zone_width = open_boundary_layers * boundary_zone.initial_condition.particle_spacing sign_ = (TrixiParticles.boundary_type_name(boundary_zone) == "inflow") ? @@ -192,9 +232,7 @@ outflow ] - @testset verbose=true "$(TrixiParticles.boundary_type_name(boundary_zone))" for boundary_zone in - boundary_zones - + @testset verbose=true "$(TrixiParticles.boundary_type_name(boundary_zone))" for boundary_zone in boundary_zones zone_width = open_boundary_layers * boundary_zone.initial_condition.particle_spacing sign_ = (TrixiParticles.boundary_type_name(boundary_zone) == "inflow") ? @@ -232,9 +270,7 @@ outflow ] - @testset verbose=true "$(TrixiParticles.boundary_type_name(boundary_zone))" for boundary_zone in - boundary_zones - + @testset verbose=true "$(TrixiParticles.boundary_type_name(boundary_zone))" for boundary_zone in boundary_zones perturb_ = TrixiParticles.boundary_type_name(boundary_zone) == "inflow" ? sqrt(eps()) : -sqrt(eps()) @@ -280,9 +316,7 @@ outflow ] - @testset verbose=true "$(TrixiParticles.boundary_type_name(boundary_zone))" for boundary_zone in - boundary_zones - + @testset verbose=true "$(TrixiParticles.boundary_type_name(boundary_zone))" for boundary_zone in boundary_zones perturb_ = TrixiParticles.boundary_type_name(boundary_zone) == "inflow" ? eps() : -eps() point4 = boundary_zone.spanning_set[1] + boundary_zone.zone_origin From a431873732519aaae7b4d748d612e3cea2b5e7be Mon Sep 17 00:00:00 2001 From: LasNikas Date: Thu, 11 Dec 2025 08:28:07 +0100 Subject: [PATCH 019/134] fix unit tests --- examples/fluid/poiseuille_flow_2d.jl | 2 +- .../boundary/open_boundary/boundary_zones.jl | 2 +- .../boundary/open_boundary/boundary_zone.jl | 62 ++++++++++++++----- 3 files changed, 50 insertions(+), 16 deletions(-) diff --git a/examples/fluid/poiseuille_flow_2d.jl b/examples/fluid/poiseuille_flow_2d.jl index 1afacd81a8..5e57c7c0bd 100644 --- a/examples/fluid/poiseuille_flow_2d.jl +++ b/examples/fluid/poiseuille_flow_2d.jl @@ -29,7 +29,7 @@ open_boundary_layers = 10 # ========================================================================================== # ==== Experiment Setup -tspan = (0.0, 1.0) +tspan = (0.0, 2.0) wcsph = true domain_size = (flow_length, wall_distance) diff --git a/src/schemes/boundary/open_boundary/boundary_zones.jl b/src/schemes/boundary/open_boundary/boundary_zones.jl index 043beb8196..55b86be737 100644 --- a/src/schemes/boundary/open_boundary/boundary_zones.jl +++ b/src/schemes/boundary/open_boundary/boundary_zones.jl @@ -346,7 +346,7 @@ function create_cache_boundary_zone(initial_condition, boundary_face, face_norma face_area = norm(v2 - v1) end - if discrete_face_area > face_area + if discrete_face_area > (face_area + eps(face_area)) @warn "The sampled area of the boundary face " * "($(discrete_face_area)) is larger than the actual face area ($(face_area)). " end diff --git a/test/schemes/boundary/open_boundary/boundary_zone.jl b/test/schemes/boundary/open_boundary/boundary_zone.jl index 619c048bbe..3e1fa13118 100644 --- a/test/schemes/boundary/open_boundary/boundary_zone.jl +++ b/test/schemes/boundary/open_boundary/boundary_zone.jl @@ -1,7 +1,7 @@ @testset verbose=true "Boundary Zone" begin @testset "`show`" begin inflow = BoundaryZone(; boundary_face=([0.0, 0.0], [0.0, 1.0]), - particle_spacing=0.05, + particle_spacing=0.05, sample_points=nothing, face_normal=(1.0, 0.0), density=1.0, reference_density=0.0, reference_pressure=0.0, @@ -22,7 +22,7 @@ @test repr("text/plain", inflow) == show_box outflow = BoundaryZone(; boundary_face=([0.0, 0.0], [0.0, 1.0]), - particle_spacing=0.05, + particle_spacing=0.05, sample_points=nothing, reference_density=0.0, reference_pressure=0.0, reference_velocity=[0.0, 0.0], @@ -41,6 +41,48 @@ └──────────────────────────────────────────────────────────────────────────────────────────────────┘""" @test repr("text/plain", outflow) == show_box + + bidirectional = BoundaryZone(; boundary_face=([0.0, 0.0], [0.0, 1.0]), + particle_spacing=0.05, sample_points=nothing, + reference_density=0.0, + reference_pressure=0.0, + reference_velocity=[0.0, 0.0], + face_normal=(1.0, 0.0), density=1.0, + open_boundary_layers=4) + + show_compact = "BoundaryZone() with 80 particles" + @test repr(bidirectional) == show_compact + show_box = """ + ┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ BoundaryZone │ + │ ════════════ │ + │ boundary type: ………………………………………… bidirectional_flow │ + │ #particles: ………………………………………………… 80 │ + │ width: ……………………………………………………………… 0.2 │ + └──────────────────────────────────────────────────────────────────────────────────────────────────┘""" + + @test repr("text/plain", bidirectional) == show_box + + zone = BoundaryZone(; boundary_face=([0.0, 0.0], [1 / sqrt(2), 1 / sqrt(2)]), + particle_spacing=0.05, reference_density=0.0, + reference_pressure=0.0, + reference_velocity=[0.0, 0.0], + face_normal=normalize([-1.0, 1.0]), density=1.0, + open_boundary_layers=4) + + show_compact = "BoundaryZone() with 80 particles" + @test repr(zone) == show_compact + show_box = """ + ┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ BoundaryZone │ + │ ════════════ │ + │ boundary type: ………………………………………… bidirectional_flow │ + │ #particles: ………………………………………………… 80 │ + │ width: ……………………………………………………………… 0.2 │ + │ cross sectional area: ……………………… 1.0 │ + └──────────────────────────────────────────────────────────────────────────────────────────────────┘""" + + @test repr("text/plain", zone) == show_box end @testset verbose=true "Illegal Inputs" begin @@ -127,9 +169,7 @@ outflow ] - @testset verbose=true "$(TrixiParticles.boundary_type_name(boundary_zone))" for boundary_zone in - boundary_zones - + @testset verbose=true "$(TrixiParticles.boundary_type_name(boundary_zone))" for boundary_zone in boundary_zones zone_width = open_boundary_layers * boundary_zone.initial_condition.particle_spacing sign_ = (TrixiParticles.boundary_type_name(boundary_zone) == "inflow") ? @@ -192,9 +232,7 @@ outflow ] - @testset verbose=true "$(TrixiParticles.boundary_type_name(boundary_zone))" for boundary_zone in - boundary_zones - + @testset verbose=true "$(TrixiParticles.boundary_type_name(boundary_zone))" for boundary_zone in boundary_zones zone_width = open_boundary_layers * boundary_zone.initial_condition.particle_spacing sign_ = (TrixiParticles.boundary_type_name(boundary_zone) == "inflow") ? @@ -232,9 +270,7 @@ outflow ] - @testset verbose=true "$(TrixiParticles.boundary_type_name(boundary_zone))" for boundary_zone in - boundary_zones - + @testset verbose=true "$(TrixiParticles.boundary_type_name(boundary_zone))" for boundary_zone in boundary_zones perturb_ = TrixiParticles.boundary_type_name(boundary_zone) == "inflow" ? sqrt(eps()) : -sqrt(eps()) @@ -280,9 +316,7 @@ outflow ] - @testset verbose=true "$(TrixiParticles.boundary_type_name(boundary_zone))" for boundary_zone in - boundary_zones - + @testset verbose=true "$(TrixiParticles.boundary_type_name(boundary_zone))" for boundary_zone in boundary_zones perturb_ = TrixiParticles.boundary_type_name(boundary_zone) == "inflow" ? eps() : -eps() point4 = boundary_zone.spanning_set[1] + boundary_zone.zone_origin From 9339bcbe7a217f84e59a819ca91b17e8fbefb647 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Thu, 11 Dec 2025 08:28:07 +0100 Subject: [PATCH 020/134] fix unit tests --- examples/fluid/poiseuille_flow_2d.jl | 2 +- .../boundary/open_boundary/boundary_zones.jl | 2 +- .../boundary/open_boundary/boundary_zone.jl | 46 ++++++++++++++++++- 3 files changed, 46 insertions(+), 4 deletions(-) diff --git a/examples/fluid/poiseuille_flow_2d.jl b/examples/fluid/poiseuille_flow_2d.jl index 1afacd81a8..5e57c7c0bd 100644 --- a/examples/fluid/poiseuille_flow_2d.jl +++ b/examples/fluid/poiseuille_flow_2d.jl @@ -29,7 +29,7 @@ open_boundary_layers = 10 # ========================================================================================== # ==== Experiment Setup -tspan = (0.0, 1.0) +tspan = (0.0, 2.0) wcsph = true domain_size = (flow_length, wall_distance) diff --git a/src/schemes/boundary/open_boundary/boundary_zones.jl b/src/schemes/boundary/open_boundary/boundary_zones.jl index 043beb8196..55b86be737 100644 --- a/src/schemes/boundary/open_boundary/boundary_zones.jl +++ b/src/schemes/boundary/open_boundary/boundary_zones.jl @@ -346,7 +346,7 @@ function create_cache_boundary_zone(initial_condition, boundary_face, face_norma face_area = norm(v2 - v1) end - if discrete_face_area > face_area + if discrete_face_area > (face_area + eps(face_area)) @warn "The sampled area of the boundary face " * "($(discrete_face_area)) is larger than the actual face area ($(face_area)). " end diff --git a/test/schemes/boundary/open_boundary/boundary_zone.jl b/test/schemes/boundary/open_boundary/boundary_zone.jl index 619c048bbe..8542fd554d 100644 --- a/test/schemes/boundary/open_boundary/boundary_zone.jl +++ b/test/schemes/boundary/open_boundary/boundary_zone.jl @@ -1,7 +1,7 @@ @testset verbose=true "Boundary Zone" begin @testset "`show`" begin inflow = BoundaryZone(; boundary_face=([0.0, 0.0], [0.0, 1.0]), - particle_spacing=0.05, + particle_spacing=0.05, sample_points=nothing, face_normal=(1.0, 0.0), density=1.0, reference_density=0.0, reference_pressure=0.0, @@ -22,7 +22,7 @@ @test repr("text/plain", inflow) == show_box outflow = BoundaryZone(; boundary_face=([0.0, 0.0], [0.0, 1.0]), - particle_spacing=0.05, + particle_spacing=0.05, sample_points=nothing, reference_density=0.0, reference_pressure=0.0, reference_velocity=[0.0, 0.0], @@ -41,6 +41,48 @@ └──────────────────────────────────────────────────────────────────────────────────────────────────┘""" @test repr("text/plain", outflow) == show_box + + bidirectional = BoundaryZone(; boundary_face=([0.0, 0.0], [0.0, 1.0]), + particle_spacing=0.05, sample_points=nothing, + reference_density=0.0, + reference_pressure=0.0, + reference_velocity=[0.0, 0.0], + face_normal=(1.0, 0.0), density=1.0, + open_boundary_layers=4) + + show_compact = "BoundaryZone() with 80 particles" + @test repr(bidirectional) == show_compact + show_box = """ + ┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ BoundaryZone │ + │ ════════════ │ + │ boundary type: ………………………………………… bidirectional_flow │ + │ #particles: ………………………………………………… 80 │ + │ width: ……………………………………………………………… 0.2 │ + └──────────────────────────────────────────────────────────────────────────────────────────────────┘""" + + @test repr("text/plain", bidirectional) == show_box + + zone = BoundaryZone(; boundary_face=([0.0, 0.0], [1 / sqrt(2), 1 / sqrt(2)]), + particle_spacing=0.05, reference_density=0.0, + reference_pressure=0.0, + reference_velocity=[0.0, 0.0], + face_normal=normalize([-1.0, 1.0]), density=1.0, + open_boundary_layers=4) + + show_compact = "BoundaryZone() with 80 particles" + @test repr(zone) == show_compact + show_box = """ + ┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ BoundaryZone │ + │ ════════════ │ + │ boundary type: ………………………………………… bidirectional_flow │ + │ #particles: ………………………………………………… 80 │ + │ width: ……………………………………………………………… 0.2 │ + │ cross sectional area: ……………………… 1.0 │ + └──────────────────────────────────────────────────────────────────────────────────────────────────┘""" + + @test repr("text/plain", zone) == show_box end @testset verbose=true "Illegal Inputs" begin From d8cda93e7d425300e555074c107aa4f3214ccc57 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Thu, 11 Dec 2025 09:39:52 +0100 Subject: [PATCH 021/134] fix doc tests --- src/schemes/boundary/open_boundary/boundary_zones.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/schemes/boundary/open_boundary/boundary_zones.jl b/src/schemes/boundary/open_boundary/boundary_zones.jl index 55b86be737..6095ec71d1 100644 --- a/src/schemes/boundary/open_boundary/boundary_zones.jl +++ b/src/schemes/boundary/open_boundary/boundary_zones.jl @@ -145,6 +145,7 @@ bidirectional_flow = BoundaryZone(; boundary_face=face_vertices, face_normal, │ boundary type: ………………………………………… bidirectional_flow │ │ #particles: ………………………………………………… 234 │ │ width: ……………………………………………………………… 0.4 │ +│ cross sectional area: ……………………… 1.0000000000000002 │ └──────────────────────────────────────────────────────────────────────────────────────────────────┘ ``` From be8a23ed944d8ed6f1960e01102074c67f7b34ba Mon Sep 17 00:00:00 2001 From: LasNikas Date: Thu, 11 Dec 2025 09:58:50 +0100 Subject: [PATCH 022/134] better docs --- src/schemes/boundary/open_boundary/boundary_zones.jl | 11 +++++++---- src/schemes/boundary/open_boundary/system.jl | 4 ++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/schemes/boundary/open_boundary/boundary_zones.jl b/src/schemes/boundary/open_boundary/boundary_zones.jl index 6095ec71d1..0a9577c16b 100644 --- a/src/schemes/boundary/open_boundary/boundary_zones.jl +++ b/src/schemes/boundary/open_boundary/boundary_zones.jl @@ -82,8 +82,10 @@ There are three ways to specify the actual shape of the boundary zone: - For `EntropicallyDampedSPHSystem`: Use the initial pressure from the `InitialCondition` - For `WeaklyCompressibleSPHSystem`: Use the background pressure from the equation of state - `sample_points=:default`: Either `:default` to automatically generate sample points on the boundary face (default), - or a matrix of dimensions `(ndims, n_points)` containing sample points + or a matrix of dimensions `(ndims, npoints)` containing sample points on the boundary face used to compute the volumetric flow rate. + Each sample point represents a discrete area of `particle_spacing^(ndims-1)`. + Therefore, `sample_points` must form a regular grid. Set to `nothing` to skip sampling. !!! note "Note" @@ -332,8 +334,6 @@ function create_cache_boundary_zone(initial_condition, boundary_face, face_norma "`ndims(initial_condition)` rows")) end - # TODO: Check if regular grid? - sample_points_ = sample_points end @@ -347,6 +347,9 @@ function create_cache_boundary_zone(initial_condition, boundary_face, face_norma face_area = norm(v2 - v1) end + # We only check if the discretized area exceeds the boundary face area. + # For 3D boundary zones with complex or non-rectangular flow profiles + # (e.g., pipe flow), the cross-sectional area can legitimately be smaller then the boundary face area. if discrete_face_area > (face_area + eps(face_area)) @warn "The sampled area of the boundary face " * "($(discrete_face_area)) is larger than the actual face area ($(face_area)). " @@ -509,7 +512,7 @@ function update_boundary_zone_indices!(system, u, boundary_zones, semi) # - Floating-point rounding when a particle lies almost exactly on the `boundary_face` # during transition, causing a reset just outside the zone # (fixed in https://github.com/trixi-framework/TrixiParticles.jl/pull/997). - @assert system.boundary_zone_indices[particle] != 0 "No boundary zone found for active buffer particle" + @assert system.boundary_zone_indices[particle]!=0 "No boundary zone found for active buffer particle" end return system diff --git a/src/schemes/boundary/open_boundary/system.jl b/src/schemes/boundary/open_boundary/system.jl index 648a97cb03..0e09c669c0 100644 --- a/src/schemes/boundary/open_boundary/system.jl +++ b/src/schemes/boundary/open_boundary/system.jl @@ -12,8 +12,8 @@ Open boundary system for in- and outflow particles. - `fluid_system`: The corresponding fluid system - `boundary_model`: Boundary model (see [Open Boundary Models](@ref open_boundary_models)) - `calculate_flow_rate=false`: Set to `true` to calculate the volumetric flow rate through each boundary zone. - Default is `false`. -- `buffer_size`: Number of buffer particles. + Default is `false`. To ensure accurate flow rate computation, the cross-sectional area + of the `boundary_zone` must be properly sampled. See `sample_points` in [`BoundaryZone`](@ref). - `pressure_acceleration`: Pressure acceleration formulation for the system. Required only when using [`BoundaryModelDynamicalPressureZhang`](@ref). Defaults to the formulation from `fluid_system` if applicable; otherwise, `nothing`. From 8aff117fcb73723e8b58ebae76f8dc2f3931bdd7 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Fri, 12 Dec 2025 15:35:33 +0100 Subject: [PATCH 023/134] use stored type --- src/io/read_vtk.jl | 10 ++++---- test/io/read_vtk.jl | 56 +++++++++++++++++++++++++++++++++++++-------- 2 files changed, 53 insertions(+), 13 deletions(-) diff --git a/src/io/read_vtk.jl b/src/io/read_vtk.jl index 884588033b..d7a578c25e 100644 --- a/src/io/read_vtk.jl +++ b/src/io/read_vtk.jl @@ -33,18 +33,20 @@ ic = vtk2trixi(joinpath("out", "rectangular.vtu")) └──────────────────────────────────────────────────────────────────────────────────────────────────┘ ``` """ -function vtk2trixi(file; element_type=Float64, coordinates_eltype=Float64) - ELTYPE = element_type - cELTYPE = coordinates_eltype +function vtk2trixi(file; element_type=:default, coordinates_eltype=Float64) vtk_file = ReadVTK.VTKFile(file) # Retrieve data fields (e.g., pressure, velocity, ...) point_data = ReadVTK.get_point_data(vtk_file) field_data = ReadVTK.get_field_data(vtk_file) + point_coords = ReadVTK.get_points(vtk_file) + + cELTYPE = coordinates_eltype + ELTYPE = element_type === :default ? eltype(point_coords) : element_type # Retrieve fields ndims = first(ReadVTK.get_data(field_data["ndims"])) - coordinates = convert.(cELTYPE, ReadVTK.get_points(vtk_file)[1:ndims, :]) + coordinates = convert.(cELTYPE, point_coords[1:ndims, :]) fields = ["velocity", "density", "pressure", "mass", "particle_spacing"] results = Dict{String, Array{Float64}}() diff --git a/test/io/read_vtk.jl b/test/io/read_vtk.jl index d82e8c50c3..1331fdc9b9 100644 --- a/test/io/read_vtk.jl +++ b/test/io/read_vtk.jl @@ -7,15 +7,53 @@ density=1000.0, pressure=900.0, mass=50.0) @testset verbose=true "`InitialCondition`" begin - trixi2vtk(expected_ic; filename="tmp_initial_condition", - output_directory=tmp_dir) - - test_ic = vtk2trixi(joinpath(tmp_dir, "tmp_initial_condition.vtu")) - - @test isapprox(expected_ic.coordinates, test_ic.coordinates, rtol=1e-5) - @test isapprox(expected_ic.velocity, test_ic.velocity, rtol=1e-5) - @test isapprox(expected_ic.density, test_ic.density, rtol=1e-5) - @test isapprox(expected_ic.pressure, test_ic.pressure, rtol=1e-5) + @testset verbose=true "`Float64`" begin + trixi2vtk(expected_ic; filename="tmp_initial_condition_64", + output_directory=tmp_dir) + file = joinpath(tmp_dir, "tmp_initial_condition_64.vtu") + test_ic = vtk2trixi(file) + + @test isapprox(expected_ic.coordinates, test_ic.coordinates, rtol=1e-5) + @test isapprox(expected_ic.velocity, test_ic.velocity, rtol=1e-5) + @test isapprox(expected_ic.density, test_ic.density, rtol=1e-5) + @test isapprox(expected_ic.pressure, test_ic.pressure, rtol=1e-5) + @test eltype(test_ic) === Float64 + @test eltype(test_ic.coordinates) === Float64 + end + + @testset verbose=true "`Float32`" begin + expected_ic_32 = InitialCondition(; + coordinates=convert.(Float32, + coordinates), + velocity=convert.(Float32, velocity), + density=1000.0f0, pressure=900.0f0, + mass=50.0f0, particle_spacing=0.1f0) + trixi2vtk(expected_ic_32; filename="tmp_initial_condition_32", + output_directory=tmp_dir) + file = joinpath(tmp_dir, "tmp_initial_condition_32.vtu") + test_ic = vtk2trixi(file) + + @test isapprox(expected_ic_32.coordinates, test_ic.coordinates, rtol=1e-5) + @test isapprox(expected_ic_32.velocity, test_ic.velocity, rtol=1e-5) + @test isapprox(expected_ic_32.density, test_ic.density, rtol=1e-5) + @test isapprox(expected_ic_32.pressure, test_ic.pressure, rtol=1e-5) + @test eltype(test_ic) === Float32 + @test eltype(test_ic.coordinates) === Float64 + end + + @testset verbose=true "Custom Element Type" begin + trixi2vtk(expected_ic; filename="tmp_initial_condition_64", + output_directory=tmp_dir) + file = joinpath(tmp_dir, "tmp_initial_condition_64.vtu") + test_ic = vtk2trixi(file, element_type=Float32, coordinates_eltype=Float32) + + @test isapprox(expected_ic.coordinates, test_ic.coordinates, rtol=1e-5) + @test isapprox(expected_ic.velocity, test_ic.velocity, rtol=1e-5) + @test isapprox(expected_ic.density, test_ic.density, rtol=1e-5) + @test isapprox(expected_ic.pressure, test_ic.pressure, rtol=1e-5) + @test eltype(test_ic) === Float32 + @test eltype(test_ic.coordinates) === Float32 + end end @testset verbose=true "`AbstractFluidSystem`" begin From 868b44922c8300cde607af1b8fb14361ec0d996b Mon Sep 17 00:00:00 2001 From: LasNikas Date: Fri, 12 Dec 2025 15:39:53 +0100 Subject: [PATCH 024/134] add docs --- src/io/read_vtk.jl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/io/read_vtk.jl b/src/io/read_vtk.jl index d7a578c25e..0638c5c21a 100644 --- a/src/io/read_vtk.jl +++ b/src/io/read_vtk.jl @@ -1,11 +1,16 @@ """ - vtk2trixi(file::String) + vtk2trixi(file::String; element_type=:default, coordinates_eltype=Float64) Load VTK file and convert data to an [`InitialCondition`](@ref). # Arguments - `file`: Name of the VTK file to be loaded. +# Keywords +- `element_type`: Element type for particle fields (`:default` keeps the type + stored in the VTK file, otherwise converted to the given type). +- `coordinates_eltype`: Element type for particle coordinates (defaults to `Float64`). + !!! warning "Experimental Implementation" This is an experimental feature and may change in any future releases. From 6e390c2524dd8d358c8dc360e7b9b685505dcf1d Mon Sep 17 00:00:00 2001 From: LasNikas Date: Fri, 12 Dec 2025 17:15:04 +0100 Subject: [PATCH 025/134] add tests --- .../boundary/open_boundary/flow_rate.jl | 64 +++++++++++++++++++ .../boundary/open_boundary/open_boundary.jl | 1 + 2 files changed, 65 insertions(+) create mode 100644 test/schemes/boundary/open_boundary/flow_rate.jl diff --git a/test/schemes/boundary/open_boundary/flow_rate.jl b/test/schemes/boundary/open_boundary/flow_rate.jl new file mode 100644 index 0000000000..9bbaf0e277 --- /dev/null +++ b/test/schemes/boundary/open_boundary/flow_rate.jl @@ -0,0 +1,64 @@ +@testset verbose=true "Calculate Flow Rate" begin + particle_spacing = 0.01 + + # Define a parabolic velocity profile + velocity_function(pos) = [4 * (pos[2] - 0.5) * (1.5 - pos[2]), 0.0] + + # Create fluid domain with the specified velocity profile + n_particles_y = round(Int, 2 / particle_spacing) + fluid = RectangularShape(particle_spacing, (4, n_particles_y), (0.0, 0.0), + density=1000.0, velocity=velocity_function) + + smoothing_length = 1.3 * particle_spacing + smoothing_kernel = WendlandC2Kernel{ndims(fluid)}() + fluid_system = EntropicallyDampedSPHSystem(fluid, smoothing_kernel, smoothing_length, + 1.0) + fluid_system.cache.density .= fluid.density + + # Use a smaller cross-sectional area to test user-defined area functionality + # and to perform interpolation within an embedded domain. + # (In a simulation with solid boundaries, wall velocities would also be included.) + sample_points = RectangularShape(particle_spacing, (1, round(Int, n_particles_y / 2)), + (0.0, 0.5), density=1.0).coordinates + + zone = BoundaryZone(; boundary_face=([0.0, 0.0], [0.0, 2.0]), + face_normal=(-1.0, 0.0), open_boundary_layers=10, density=1000.0, + particle_spacing, sample_points=sample_points, + reference_velocity=(pos, t) -> velocity_function(pos)) + + open_boundary = OpenBoundarySystem(zone; fluid_system, + boundary_model=BoundaryModelMirroringTafuni(), + calculate_flow_rate=true, buffer_size=0) + + semi = Semidiscretization(fluid_system, open_boundary) + TrixiParticles.initialize_neighborhood_searches!(semi) + TrixiParticles.initialize!(open_boundary, semi) + + # Set up ODE for initial conditions + ode = semidiscretize(semi, (0, 1)) + v_ode, u_ode = ode.u0.x + + boundary_zone = first(open_boundary.boundary_zones) + + @testset verbose=true "Velocity Interpolation" begin + TrixiParticles.interpolate_velocity!(open_boundary, boundary_zone, + v_ode, u_ode, semi) + + coords = reinterpret(reshape, SVector{2, Float64}, + boundary_zone.cache.sample_points) + vels_analytic = first.(velocity_function.(coords)) + vels_interpolated = first.(reinterpret(reshape, SVector{2, Float64}, + boundary_zone.cache.sample_velocity)) + + @test isapprox(vels_interpolated, vels_analytic, rtol=1e-3) + end + + @testset verbose=true "Flow Rate Calculation" begin + TrixiParticles.calculate_flow_rate!(open_boundary, v_ode, u_ode, semi) + + Q_analytic = zone.cache.cross_sectional_area * (2 / 3) + Q_calculated = first(open_boundary.cache.boundary_zones_flow_rate)[] + + @test isapprox(Q_analytic, Q_calculated; rtol=1e-3) + end +end diff --git a/test/schemes/boundary/open_boundary/open_boundary.jl b/test/schemes/boundary/open_boundary/open_boundary.jl index 78c73345e4..74fd8098df 100644 --- a/test/schemes/boundary/open_boundary/open_boundary.jl +++ b/test/schemes/boundary/open_boundary/open_boundary.jl @@ -2,3 +2,4 @@ include("characteristic_variables.jl") include("mirroring.jl") include("boundary_zone.jl") include("pressure_model.jl") +include("flow_rate.jl") From 6121e7d8b3c071027f46949a578b340e40c895bc Mon Sep 17 00:00:00 2001 From: LasNikas Date: Fri, 12 Dec 2025 17:43:17 +0100 Subject: [PATCH 026/134] fix formatting --- src/schemes/boundary/open_boundary/boundary_zones.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/schemes/boundary/open_boundary/boundary_zones.jl b/src/schemes/boundary/open_boundary/boundary_zones.jl index 0a9577c16b..7f15f92def 100644 --- a/src/schemes/boundary/open_boundary/boundary_zones.jl +++ b/src/schemes/boundary/open_boundary/boundary_zones.jl @@ -349,7 +349,7 @@ function create_cache_boundary_zone(initial_condition, boundary_face, face_norma # We only check if the discretized area exceeds the boundary face area. # For 3D boundary zones with complex or non-rectangular flow profiles - # (e.g., pipe flow), the cross-sectional area can legitimately be smaller then the boundary face area. + # (e.g., pipe flow), the cross-sectional area can legitimately be smaller than the boundary face area. if discrete_face_area > (face_area + eps(face_area)) @warn "The sampled area of the boundary face " * "($(discrete_face_area)) is larger than the actual face area ($(face_area)). " @@ -512,7 +512,7 @@ function update_boundary_zone_indices!(system, u, boundary_zones, semi) # - Floating-point rounding when a particle lies almost exactly on the `boundary_face` # during transition, causing a reset just outside the zone # (fixed in https://github.com/trixi-framework/TrixiParticles.jl/pull/997). - @assert system.boundary_zone_indices[particle]!=0 "No boundary zone found for active buffer particle" + @assert system.boundary_zone_indices[particle] != 0 "No boundary zone found for active buffer particle" end return system From 7369f71c127077a8d059ca29fb3a4cd12eccd0e5 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Sat, 13 Dec 2025 09:54:42 +0100 Subject: [PATCH 027/134] fix eltype --- src/schemes/boundary/open_boundary/boundary_zones.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/schemes/boundary/open_boundary/boundary_zones.jl b/src/schemes/boundary/open_boundary/boundary_zones.jl index 7f15f92def..8e42b7088a 100644 --- a/src/schemes/boundary/open_boundary/boundary_zones.jl +++ b/src/schemes/boundary/open_boundary/boundary_zones.jl @@ -326,15 +326,15 @@ function create_cache_boundary_zone(initial_condition, boundary_face, face_norma (; particle_spacing) = initial_condition area_increment = particle_spacing^(ndims(initial_condition) - 1) if sample_points === :default - sample_points_ = extrude_geometry(boundary_face; particle_spacing, density=Inf, - direction=(-face_normal), n_extrude=1).coordinates + points = extrude_geometry(boundary_face; particle_spacing, density=Inf, + direction=(-face_normal), n_extrude=1).coordinates + sample_points_ = convert.(eltype(initial_condition), points) else if !(sample_points isa Matrix && size(sample_points, 1) == ndims(initial_condition)) throw(ArgumentError("`sample_points` must be a matrix with " * "`ndims(initial_condition)` rows")) end - - sample_points_ = sample_points + sample_points_ = convert.(eltype(initial_condition), sample_points) end discrete_face_area = area_increment * size(sample_points_, 2) From a9123267463a4a0a38f2355c6e226b34aa740e3c Mon Sep 17 00:00:00 2001 From: LasNikas Date: Tue, 16 Dec 2025 09:09:06 +0100 Subject: [PATCH 028/134] revise #959 --- src/io/read_vtk.jl | 13 ++++++---- test/io/read_vtk.jl | 61 ++++++++++++++++++++------------------------- 2 files changed, 35 insertions(+), 39 deletions(-) diff --git a/src/io/read_vtk.jl b/src/io/read_vtk.jl index acce879a04..e30a28d76c 100644 --- a/src/io/read_vtk.jl +++ b/src/io/read_vtk.jl @@ -2,7 +2,7 @@ vtk2trixi(file::String; element_type=:default, coordinates_eltype=Float64, custom_quantities...) -Load VTK file and convert data to a NamedTuple. +Load VTK file and convert data to a `NamedTuple`. # Arguments - `file`: Name of the VTK file to be loaded. @@ -76,14 +76,17 @@ function vtk2trixi(file; element_type=:default, coordinates_eltype=Float64, results[:particle_spacing] results[:coordinates] = coordinates results[:time] = "time" in keys(field_data) ? - first(ReadVTK.get_data(field_data["time"])) : 0.0 + first(ReadVTK.get_data(field_data["time"])) : zero(ELTYPE) - for (key, quantity) in custom_quantities + for (key, quantity_) in custom_quantities + quantity = string(quantity_) if quantity in keys(point_data) results[key] = ReadVTK.get_data(point_data[quantity]) - end - if quantity in keys(field_data) + elseif quantity in keys(field_data) results[key] = first(ReadVTK.get_data(field_data[quantity])) + else + throw(ArgumentError("Custom quantity '$quantity' not found in VTK file. " * + "Make sure it was included during the simulation.")) end end diff --git a/test/io/read_vtk.jl b/test/io/read_vtk.jl index a4ac246dde..43bac75794 100644 --- a/test/io/read_vtk.jl +++ b/test/io/read_vtk.jl @@ -7,11 +7,9 @@ density=1000.0, pressure=900.0, mass=50.0) expected_cq_scalar = 3.0 - expected_cq_vector = fill(expected_cq_scalar, - size(expected_data.coordinates, 2)) + expected_cq_vector = fill(expected_cq_scalar, nparticles(expected_data)) scalar_cq_function(system, data, t) = expected_cq_scalar - vector_cq_function(system, data, - t) = fill(expected_cq_scalar, nparticles(system)) + vector_cq_function(system, data, t) = fill(expected_cq_scalar, nparticles(system)) @testset verbose=true "`InitialCondition`" begin @testset verbose=true "`Float64`" begin @@ -24,6 +22,7 @@ @test isapprox(expected_data.velocity, data.velocity, rtol=1e-5) @test isapprox(expected_data.density, data.density, rtol=1e-5) @test isapprox(expected_data.pressure, data.pressure, rtol=1e-5) + @test all(key -> eltype(data[key]) === Float64, keys(data)) @test eltype(data.coordinates) === Float64 end @@ -43,6 +42,8 @@ @test isapprox(expected_ic_32.velocity, data.velocity, rtol=1e-5) @test isapprox(expected_ic_32.density, data.density, rtol=1e-5) @test isapprox(expected_ic_32.pressure, data.pressure, rtol=1e-5) + @test all(key -> eltype(data[key]) === Float32, + setdiff(keys(data), (:coordinates,))) @test eltype(data.coordinates) === Float64 end @@ -56,41 +57,39 @@ @test isapprox(expected_data.velocity, data.velocity, rtol=1e-5) @test isapprox(expected_data.density, data.density, rtol=1e-5) @test isapprox(expected_data.pressure, data.pressure, rtol=1e-5) + @test all(key -> eltype(data[key]) === Float32, keys(data)) @test eltype(data.coordinates) === Float32 end - @testset verbose=true "Scalar custom quantity" begin + @testset verbose=true "Scalar Custom Quantity" begin trixi2vtk(expected_data; filename="tmp_initial_condition_scalar", - output_directory=tmp_dir, - cq_scalar=expected_cq_scalar) + output_directory=tmp_dir, cq_scalar=expected_cq_scalar) # Load file containing test data test_data = vtk2trixi(joinpath(tmp_dir, "tmp_initial_condition_scalar.vtu"); - cq_scalar="cq_scalar") + cq_scalar=:cq_scalar) @test isapprox(expected_data.coordinates, test_data.coordinates, rtol=1e-5) @test isapprox(expected_data.velocity, test_data.velocity, rtol=1e-5) @test isapprox(expected_data.density, test_data.density, rtol=1e-5) @test isapprox(expected_data.pressure, test_data.pressure, rtol=1e-5) - @test isapprox(expected_cq_scalar, test_data.cq_scalar, - rtol=1e-5) + @test isapprox(expected_cq_scalar, test_data.cq_scalar, rtol=1e-5) end - @testset verbose=true "Vector custom quantity" begin + @testset verbose=true "Vector Custom Quantity" begin trixi2vtk(expected_data; filename="tmp_initial_condition_vector", output_directory=tmp_dir, cq_vector=expected_cq_vector) # Load file containing test data test_data = vtk2trixi(joinpath(tmp_dir, "tmp_initial_condition_vector.vtu"); - cq_vector="cq_vector") + cq_vector=:cq_vector) @test isapprox(expected_data.coordinates, test_data.coordinates, rtol=1e-5) @test isapprox(expected_data.velocity, test_data.velocity, rtol=1e-5) @test isapprox(expected_data.density, test_data.density, rtol=1e-5) @test isapprox(expected_data.pressure, test_data.pressure, rtol=1e-5) - @test isapprox(expected_cq_vector, test_data.cq_vector, - rtol=1e-5) + @test isapprox(expected_cq_vector, test_data.cq_vector, rtol=1e-5) end end @@ -116,33 +115,30 @@ vu_ode = (; x) dvdu_ode = (; x=(; v_ode=dv_ode, u_ode=du_ode)) - @testset verbose=true "Scalar custom quantity" begin + @testset verbose=true "Scalar Custom Quantity" begin trixi2vtk(fluid_system, dvdu_ode, vu_ode, semi, 0.0, nothing; system_name="tmp_file_fluid_scalar", - output_directory=tmp_dir, - iter=1, cq_scalar=scalar_cq_function) + output_directory=tmp_dir, iter=1, cq_scalar=scalar_cq_function) # Load file containing test data test_data = vtk2trixi(joinpath(tmp_dir, "tmp_file_fluid_scalar_1.vtu"); - cq_scalar="cq_scalar") + cq_scalar=:cq_scalar) @test isapprox(u, test_data.coordinates, rtol=1e-5) @test isapprox(v, test_data.velocity, rtol=1e-5) @test isapprox(pressure, test_data.pressure, rtol=1e-5) @test isapprox(fluid_system.cache.density, test_data.density, rtol=1e-5) - @test isapprox(expected_cq_scalar, test_data.cq_scalar, - rtol=1e-5) + @test isapprox(expected_cq_scalar, test_data.cq_scalar, rtol=1e-5) end - @testset verbose=true "Vector custom quantity" begin + @testset verbose=true "Vector Custom Quantity" begin trixi2vtk(fluid_system, dvdu_ode, vu_ode, semi, 0.0, nothing; system_name="tmp_file_fluid_vector", - output_directory=tmp_dir, - iter=1, cq_vector=vector_cq_function) + output_directory=tmp_dir, iter=1, cq_vector=vector_cq_function) # Load file containing test data test_data = vtk2trixi(joinpath(tmp_dir, "tmp_file_fluid_vector_1.vtu"); - cq_vector="cq_vector") + cq_vector=:cq_vector) @test isapprox(u, test_data.coordinates, rtol=1e-5) @test isapprox(v, test_data.velocity, rtol=1e-5) @@ -176,15 +172,14 @@ vu_ode = (; x) dvdu_ode = (; x=(; v_ode=dv_ode, u_ode=du_ode)) - @testset verbose=true "Scalar custom quantity" begin + @testset verbose=true "Scalar Custom Quantity" begin trixi2vtk(boundary_system, dvdu_ode, vu_ode, semi, 0.0, nothing; system_name="tmp_file_boundary_scalar", - output_directory=tmp_dir, - iter=1, cq_scalar=scalar_cq_function) + output_directory=tmp_dir, iter=1, cq_scalar=scalar_cq_function) # Load file containing test data test_data = vtk2trixi(joinpath(tmp_dir, "tmp_file_boundary_scalar_1.vtu"); - cq_scalar="cq_scalar") + cq_scalar=:cq_scalar) @test isapprox(boundary_system.coordinates, test_data.coordinates, rtol=1e-5) @@ -193,19 +188,17 @@ rtol=1e-5) @test isapprox(boundary_model.pressure, test_data.pressure, rtol=1e-5) @test isapprox(boundary_model.cache.density, test_data.density, rtol=1e-5) - @test isapprox(expected_cq_scalar, test_data.cq_scalar, - rtol=1e-5) + @test isapprox(expected_cq_scalar, test_data.cq_scalar, rtol=1e-5) end - @testset verbose=true "Vector custom quantity" begin + @testset verbose=true "Vector Custom Quantity" begin trixi2vtk(boundary_system, dvdu_ode, vu_ode, semi, 0.0, nothing; system_name="tmp_file_boundary_vector", - output_directory=tmp_dir, - iter=1, cq_vector=vector_cq_function) + output_directory=tmp_dir, iter=1, cq_vector=vector_cq_function) # Load file containing test data test_data = vtk2trixi(joinpath(tmp_dir, "tmp_file_boundary_vector_1.vtu"); - cq_vector="cq_vector") + cq_vector=:cq_vector) @test isapprox(boundary_system.coordinates, test_data.coordinates, rtol=1e-5) From 29b5b1a283d2d7f7582a8a92dca93f44e9e9ff73 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Tue, 6 Jan 2026 11:51:14 +0100 Subject: [PATCH 029/134] fix buffer --- src/general/buffer.jl | 4 ++-- src/schemes/boundary/open_boundary/system.jl | 8 ++++---- test/general/buffer.jl | 9 +++------ 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/general/buffer.jl b/src/general/buffer.jl index 1e2c196799..48ab0d92be 100644 --- a/src/general/buffer.jl +++ b/src/general/buffer.jl @@ -40,10 +40,10 @@ end # Dispatch by system type to handle systems that provide a buffer. @inline buffer(system) = nothing -@inline update_system_buffer!(buffer::Nothing, semi) = buffer +@inline update_system_buffer!(buffer::Nothing) = buffer # TODO `resize` allocates. Find a non-allocating version -@inline function update_system_buffer!(buffer::SystemBuffer, semi) +@inline function update_system_buffer!(buffer::SystemBuffer) (; active_particle) = buffer # TODO: Parallelize (see https://github.com/trixi-framework/TrixiParticles.jl/issues/810) diff --git a/src/schemes/boundary/open_boundary/system.jl b/src/schemes/boundary/open_boundary/system.jl index a1e7140c52..b74da7153d 100644 --- a/src/schemes/boundary/open_boundary/system.jl +++ b/src/schemes/boundary/open_boundary/system.jl @@ -405,8 +405,8 @@ function check_domain!(system, v, u, v_ode, u_ode, semi) v, u, v_fluid, u_fluid) end - update_system_buffer!(system.buffer, semi) - update_system_buffer!(fluid_system.buffer, semi) + update_system_buffer!(system.buffer) + update_system_buffer!(fluid_system.buffer) fluid_candidates .= false @@ -436,8 +436,8 @@ function check_domain!(system, v, u, v_ode, u_ode, semi) v, u, v_fluid, u_fluid) end - update_system_buffer!(system.buffer, semi) - update_system_buffer!(fluid_system.buffer, semi) + update_system_buffer!(system.buffer) + update_system_buffer!(fluid_system.buffer) # Since particles have been transferred, the neighborhood searches must be updated update_nhs!(semi, u_ode) diff --git a/test/general/buffer.jl b/test/general/buffer.jl index f9cdf25cb2..9eb6d87fac 100644 --- a/test/general/buffer.jl +++ b/test/general/buffer.jl @@ -28,16 +28,14 @@ particle_id = findfirst(==(false), system_buffer.buffer.active_particle) system_buffer.buffer.active_particle[particle_id] = true - TrixiParticles.update_system_buffer!(system_buffer.buffer, - DummySemidiscretization()) + TrixiParticles.update_system_buffer!(system_buffer.buffer) @test TrixiParticles.each_integrated_particle(system_buffer) == 1:(n_particles + 1) TrixiParticles.deactivate_particle!(system_buffer, particle_id, ones(2, particle_id)) - TrixiParticles.update_system_buffer!(system_buffer.buffer, - DummySemidiscretization()) + TrixiParticles.update_system_buffer!(system_buffer.buffer) @test TrixiParticles.each_integrated_particle(system_buffer) == 1:n_particles @@ -45,8 +43,7 @@ TrixiParticles.deactivate_particle!(system_buffer, particle_id, ones(2, particle_id)) - TrixiParticles.update_system_buffer!(system_buffer.buffer, - DummySemidiscretization()) + TrixiParticles.update_system_buffer!(system_buffer.buffer) @test TrixiParticles.each_integrated_particle(system_buffer) == setdiff(1:n_particles, particle_id) From daceea26d9c78bad972761a46c5f9c5a6eb9c18f Mon Sep 17 00:00:00 2001 From: LasNikas Date: Tue, 6 Jan 2026 12:15:49 +0100 Subject: [PATCH 030/134] first poc --- src/general/restart.jl | 94 +++++++++++++++++++ src/general/semidiscretization.jl | 25 +++-- src/schemes/boundary/open_boundary/system.jl | 45 +++++++++ .../boundary/wall_boundary/dummy_particles.jl | 4 + .../boundary/wall_boundary/monaghan_kajtar.jl | 4 + src/schemes/boundary/wall_boundary/system.jl | 17 ++++ src/schemes/fluid/fluid.jl | 45 +++++++++ .../structure/total_lagrangian_sph/system.jl | 11 +++ 8 files changed, 236 insertions(+), 9 deletions(-) create mode 100644 src/general/restart.jl diff --git a/src/general/restart.jl b/src/general/restart.jl new file mode 100644 index 0000000000..df3eaef261 --- /dev/null +++ b/src/general/restart.jl @@ -0,0 +1,94 @@ +struct RestartCondition{V, U} + v_restart::V + u_restart::U + t_restart::Real +end + +function RestartCondition(system::AbstractSystem, restart_file; precondition_values=nothing) + restart_data = vtk2trixi(restart_file) + v_restart = restart_v(system, restart_data) + u_restart = restart_u(system, restart_data) + + # TODO + t_restart = 0.0 + + precondition_system!(system, precondition_values) + + return RestartCondition(v_restart, u_restart, t_restart) +end + +function set_intial_conditions!(v0_ode, u0_ode, semi, restart_conditions) + if length(semi.systems) != length(restart_conditions) + throw(ArgumentError("Number of systems in `semi` does not match number of `restart_conditions`")) + end + + foreach_noalloc(semi.systems, restart_conditions) do (system, restart_condition) + v0_system = wrap_v(v0_ode, system, semi) + u0_system = wrap_u(u0_ode, system, semi) + + u0_system .= restart_condition.u_restart + v0_system .= restart_condition.v_restart + end +end + +function time_span(tspan, restart_conditions::RestartCondition) + return (first(restart_conditions).t_restart, tspan[2]) +end + +function write_density_and_pressure!(v_restart, system, density_calculator, + pressure, density) + return v_restart +end + +function write_density_and_pressure!(v_restart, system, + density_calculator::ContinuityDensity, + pressure, density) + v_restart[size(v_restart, 1), :] = density + + return v_restart +end + +function write_density_and_pressure!(v_restart, system::EntropicallyDampedSPHSystem, + density_calculator::ContinuityDensity, + pressure, density) + v_restart[size(v_restart, 1), :] = density + v_restart[size(v_restart, 1) - 1, :] = pressure + + return v_restart +end + +precondition_system!(system, values::Nothing) = system + +function precondition_system!(system::OpenBoundarySystem, values) + (; pressure_reference_values, boundary_zones_flow_rate) = system.cache + + if !haskey(values, :pressure_reference_values, :boundary_zones_flow_rate) + throw(ArgumentError("Missing required fields in `values` for `OpenBoundarySystem`")) + end + + foreach_noalloc(pressure_reference_values, + values.pressure_reference_values) do (pressure_model, + previous_pressure) + if isa(pressure_model, AbstractPressureModel) + pressure_model.pressure[] = previous_pressure + else + if previous_pressure != Inf + throw(ArgumentError("Expected `Inf` for non-pressure-model boundary zones")) + end + end + end + + foreach_noalloc(pressure_reference_values, + values.boundary_zones_flow_rate) do (pressure_model, + previous_flow_rate) + if isa(pressure_model, AbstractPressureModel) + pressure_model.flow_rate[] = previous_flow_rate + else + if previous_flow_rate != Inf + throw(ArgumentError("Expected `Inf` for non-pressure-model boundary zones")) + end + end + end + + return system +end diff --git a/src/general/semidiscretization.jl b/src/general/semidiscretization.jl index c350511b63..dc991aae95 100644 --- a/src/general/semidiscretization.jl +++ b/src/general/semidiscretization.jl @@ -288,7 +288,7 @@ timespan: (0.0, 1.0) u0: ([...], [...]) *this line is ignored by filter* ``` """ -function semidiscretize(semi, tspan; reset_threads=true) +function semidiscretize(semi, tspan; reset_threads=true, restart_conditions=nothing) (; systems) = semi # Check that all systems have the same eltype @@ -329,13 +329,7 @@ function semidiscretize(semi, tspan; reset_threads=true) end # Set initial condition - foreach_system(semi) do system - u0_system = wrap_u(u0_ode, system, semi) - v0_system = wrap_v(v0_ode, system, semi) - - write_u0!(u0_system, system) - write_v0!(v0_system, system) - end + set_intial_conditions!(v0_ode, u0_ode, semi, restart_conditions) # TODO initialize after adapting to the GPU. # Requires https://github.com/trixi-framework/PointNeighbors.jl/pull/86. @@ -371,9 +365,22 @@ function semidiscretize(semi, tspan; reset_threads=true) # Reset callback flag that will be set by the `UpdateCallback` semi_new.update_callback_used[] = false - return DynamicalODEProblem(kick!, drift!, v0_ode, u0_ode, tspan, semi_new) + return DynamicalODEProblem(kick!, drift!, v0_ode, u0_ode, + time_span(tspan, restart_conditions), semi_new) end +function set_intial_conditions!(v0_ode, u0_ode, semi, restart_conditions::Nothing) + foreach_system(semi) do system + v0_system = wrap_v(v0_ode, system, semi) + u0_system = wrap_u(u0_ode, system, semi) + + write_v0!(v0_system, system) + write_u0!(u0_system, system) + end +end + +time_span(tspan, restart_conditions::Nothing) = tspan + """ restart_with!(semi, sol) diff --git a/src/schemes/boundary/open_boundary/system.jl b/src/schemes/boundary/open_boundary/system.jl index b74da7153d..c18416989c 100644 --- a/src/schemes/boundary/open_boundary/system.jl +++ b/src/schemes/boundary/open_boundary/system.jl @@ -659,3 +659,48 @@ function interpolate_velocity!(system::OpenBoundarySystem, boundary_zone, return system end + +function restart_u(system::OpenBoundarySystem, data) + coords_total = zeros(eltype(system), u_nvariables(system), + n_integrated_particles(system)) + coords_total .= eltype(system)(1e16) + system.buffer.active_particle .= false + + coords_active = data.coordinates + + for particle in axes(coords_active, 2) + system.buffer.active_particle[particle] = true + for dim in 1:ndims(system) + coords_total[dim, particle] = coords_active[dim, particle] + end + end + + update_system_buffer!(system.buffer) + + return coords_total +end + +function restart_v(system::OpenBoundarySystem, data) + velocity_total = zeros(eltype(system), v_nvariables(system), + n_integrated_particles(system)) + velocity_total .= eltype(system)(1e16) + + system.buffer.active_particle .= false + + velocity_active = zeros(eltype(system), v_nvariables(system), size(data.velocity, 2)) + + velocity_active[1:ndims(system), :] = data.velocity + write_density_and_pressure!(velocity_active, system.fluid_system, + density_calculator(system), data.pressure, data.density) + + for particle in axes(velocity_active, 2) + system.buffer.active_particle[particle] = true + for i in 1:axes(velocity_active, 1) + velocity_total[i, particle] = velocity_active[i, particle] + end + end + + update_system_buffer!(system.buffer) + + return coords_total +end diff --git a/src/schemes/boundary/wall_boundary/dummy_particles.jl b/src/schemes/boundary/wall_boundary/dummy_particles.jl index 192eb5fee1..bc26b147fa 100644 --- a/src/schemes/boundary/wall_boundary/dummy_particles.jl +++ b/src/schemes/boundary/wall_boundary/dummy_particles.jl @@ -697,3 +697,7 @@ end @inline function correction_matrix(system::AbstractBoundarySystem, particle) extract_smatrix(system.boundary_model.cache.correction_matrix, system, particle) end + +@inline function density_calculator(system::WallBoundarySystem{<:BoundaryModelDummyParticles}) + return system.boundary_model.density_calculator +end diff --git a/src/schemes/boundary/wall_boundary/monaghan_kajtar.jl b/src/schemes/boundary/wall_boundary/monaghan_kajtar.jl index 1df9a38cf9..f5f3b9005a 100644 --- a/src/schemes/boundary/wall_boundary/monaghan_kajtar.jl +++ b/src/schemes/boundary/wall_boundary/monaghan_kajtar.jl @@ -119,3 +119,7 @@ end # Nothing to do in the update step return boundary_model end + +@inline function density_calculator(system::WallBoundarySystem{<:BoundaryModelMonaghanKajtar}) + return nothing +end diff --git a/src/schemes/boundary/wall_boundary/system.jl b/src/schemes/boundary/wall_boundary/system.jl index 151787e7aa..ce18344eea 100644 --- a/src/schemes/boundary/wall_boundary/system.jl +++ b/src/schemes/boundary/wall_boundary/system.jl @@ -246,6 +246,23 @@ function restart_with!(system::WallBoundarySystem{<:BoundaryModelDummyParticles{ return system end +function restart_u(system::WallBoundarySystem, data) + if n_integrated_particles(system) > 0 + throw(ArgumentError("`WallBoundarySystem` does not support integrated particle coordinates")) + end + + return zeros(eltype(system), u_nvariables(system), n_integrated_particles(system)) +end + +function restart_v(system::WallBoundarySystem, data) + v_ode = zeros(eltype(system), v_nvariables(system), n_integrated_particles(system)) + + write_density_and_pressure!(v_ode, system, density_calculator(system), + data.pressure, data.density) + + return coords_total +end + # To incorporate the effect at boundaries in the viscosity term of the RHS, the neighbor # viscosity model has to be used. @inline function viscosity_model(system::WallBoundarySystem, diff --git a/src/schemes/fluid/fluid.jl b/src/schemes/fluid/fluid.jl index 35d14b5e04..0b9387baaf 100644 --- a/src/schemes/fluid/fluid.jl +++ b/src/schemes/fluid/fluid.jl @@ -220,6 +220,51 @@ end return nothing end +function restart_u(system::AbstractFluidSystem, data) + coords_total = zeros(eltype(system), u_nvariables(system), + n_integrated_particles(system)) + coords_total .= eltype(system)(1e16) + isnothing(buffer(system)) || system.buffer.active_particle .= false + + coords_active = data.coordinates + + for particle in axes(coords_active, 2) + isnothing(buffer(system)) || system.buffer.active_particle[particle] = true + for dim in 1:ndims(system) + coords_total[dim, particle] = coords_active[dim, particle] + end + end + + update_system_buffer!(system.buffer) + + return coords_total +end + +function restart_v(system::AbstractFluidSystem, data) + velocity_total = zeros(eltype(system), v_nvariables(system), + n_integrated_particles(system)) + velocity_total .= eltype(system)(1e16) + + isnothing(buffer(system)) || system.buffer.active_particle .= false + + velocity_active = zeros(eltype(system), v_nvariables(system), size(data.velocity, 2)) + + velocity_active[1:ndims(system), :] = data.velocity + write_density_and_pressure!(velocity_active, system, density_calculator(system), + data.pressure, data.density) + + for particle in axes(velocity_active, 2) + isnothing(buffer(system)) || system.buffer.active_particle[particle] = true + for i in 1:axes(velocity_active, 1) + velocity_total[i, particle] = velocity_active[i, particle] + end + end + + update_system_buffer!(system.buffer) + + return coords_total +end + function system_data(system::AbstractFluidSystem, dv_ode, du_ode, v_ode, u_ode, semi) (; mass) = system diff --git a/src/schemes/structure/total_lagrangian_sph/system.jl b/src/schemes/structure/total_lagrangian_sph/system.jl index ae1b7735db..18f4886a20 100644 --- a/src/schemes/structure/total_lagrangian_sph/system.jl +++ b/src/schemes/structure/total_lagrangian_sph/system.jl @@ -485,6 +485,17 @@ function restart_with!(system::TotalLagrangianSPHSystem, v, u) restart_with!(system, system.boundary_model, v, u) end +function restart_u(system::AbstractStructureSystem, data) + # TODO: Is this correct? + # data.coordinates = coords_integrated + return data.coordinates[:, each_integrated_particle(system)] +end + +function restart_v(system::AbstractStructureSystem, data) + # TODO: Is this correct? + return data.velocity[:, each_integrated_particle(system)] +end + # An explanation of these equation can be found in # J. Lubliner, 2008. Plasticity theory. # See here below Equation 5.3.21 for the equation for the equivalent stress. From e9750b0b7e2f7235c982de81d642b041e919fc18 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Tue, 6 Jan 2026 15:19:23 +0100 Subject: [PATCH 031/134] add show --- src/general/restart.jl | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/src/general/restart.jl b/src/general/restart.jl index c2a5526296..efbef368c2 100644 --- a/src/general/restart.jl +++ b/src/general/restart.jl @@ -4,8 +4,14 @@ struct RestartCondition{V, U} t_restart::Real end -function RestartCondition(system::AbstractSystem, restart_file; precondition_values=nothing) - restart_data = vtk2trixi(restart_file) +function RestartCondition(system::AbstractSystem, filename; output_directory="out", + precondition_values=nothing) + @autoinfiltrate + if !occursin(vtkname(system), basename(splitext(filename)[1])) + throw(ArgumentError("Filename '$filename' does not seem to correspond to system of type $(nameof(typeof(system))).")) + end + + restart_data = vtk2trixi(joinpath(output_directory, filename)) v_restart = restart_v(system, restart_data) u_restart = restart_u(system, restart_data) @@ -16,6 +22,26 @@ function RestartCondition(system::AbstractSystem, restart_file; precondition_val return RestartCondition(v_restart, u_restart, restart_data.time) end +function Base.show(io::IO, rc::RestartCondition) + @nospecialize rc # reduce precompilation time + + print(io, "RestartCondition{}()") +end + +function Base.show(io::IO, ::MIME"text/plain", rc::RestartCondition) + @nospecialize rc # reduce precompilation time + + if get(io, :compact, false) + show(io, rc) + else + summary_header(io, "RestartCondition") + summary_line(io, "#particles u", "$(size(rc.u_restart, 2))") + summary_line(io, "#particles v", "$(size(rc.v_restart, 2))") + summary_line(io, "eltype", "$(eltype(rc.v_restart))") + summary_footer(io) + end +end + function set_intial_conditions!(v0_ode, u0_ode, semi, restart_conditions) if length(semi.systems) != length(restart_conditions) throw(ArgumentError("Number of systems in `semi` does not match number of `restart_conditions`")) From 695fe6cf07e05a027c1395f779c7dffe4ab028de Mon Sep 17 00:00:00 2001 From: LasNikas Date: Tue, 6 Jan 2026 15:20:36 +0100 Subject: [PATCH 032/134] remove mass --- src/io/read_vtk.jl | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/io/read_vtk.jl b/src/io/read_vtk.jl index e30a28d76c..6f0701f560 100644 --- a/src/io/read_vtk.jl +++ b/src/io/read_vtk.jl @@ -56,7 +56,7 @@ function vtk2trixi(file; element_type=:default, coordinates_eltype=Float64, ndims = first(ReadVTK.get_data(field_data["ndims"])) coordinates = convert.(cELTYPE, point_coords[1:ndims, :]) - fields = [:velocity, :density, :pressure, :mass, :particle_spacing] + fields = [:velocity, :density, :pressure, :particle_spacing] for field in fields # Look for any key that contains the field name all_keys = keys(point_data) @@ -64,10 +64,7 @@ function vtk2trixi(file; element_type=:default, coordinates_eltype=Float64, if idx !== nothing results[field] = convert.(ELTYPE, ReadVTK.get_data(point_data[all_keys[idx]])) else - # Use zeros as default values when a field is missing - results[field] = string(field) in ["mass"] ? - zeros(ELTYPE, size(coordinates, 2)) : zero(coordinates) - @info "No '$field' field found in VTK file. Will be set to zero." + @info "No '$field' field found in VTK file" end end From 2d8bce8925f493361585700b8513cffa38ed7fdd Mon Sep 17 00:00:00 2001 From: LasNikas Date: Tue, 6 Jan 2026 16:00:00 +0100 Subject: [PATCH 033/134] add preconditioning --- src/general/restart.jl | 41 +++----------------- src/io/write_vtk.jl | 8 ++++ src/schemes/boundary/open_boundary/system.jl | 18 +++++++++ 3 files changed, 31 insertions(+), 36 deletions(-) diff --git a/src/general/restart.jl b/src/general/restart.jl index 87c8b5b905..e4c85ec0c6 100644 --- a/src/general/restart.jl +++ b/src/general/restart.jl @@ -4,19 +4,17 @@ struct RestartCondition{V, U} t_restart::Real end -function RestartCondition(system::AbstractSystem, filename; output_directory="out", - precondition_values=nothing) +function RestartCondition(system::AbstractSystem, filename; output_directory="out") if !occursin(vtkname(system), basename(splitext(filename)[1])) throw(ArgumentError("Filename '$filename' does not seem to correspond to system of type $(nameof(typeof(system))).")) end - restart_data = vtk2trixi(joinpath(output_directory, filename)) + restart_file = joinpath(output_directory, filename) + restart_data = vtk2trixi(restart_file) v_restart = restart_v(system, restart_data) u_restart = restart_u(system, restart_data) - if !isnothing(precondition_values) - precondition_system!(system, precondition_values) - end + precondition_system!(system, restart_file) return RestartCondition(v_restart, u_restart, restart_data.time) end @@ -87,33 +85,4 @@ function write_density_and_pressure!(v_restart, system::EntropicallyDampedSPHSys return v_restart end -precondition_system!(system, values) = system - -function precondition_system!(system::OpenBoundarySystem, values) - (; pressure_reference_values, boundary_zones_flow_rate) = system.cache - - if !haskey(values, :pressure_reference_values) && - !haskey(values, :boundary_zones_flow_rate) - throw(ArgumentError("Missing required fields in `values` for `OpenBoundarySystem`")) - end - - foreach_noalloc(pressure_reference_values, - values.pressure_reference_values) do (pressure_model, - pressure_restart) - if isa(pressure_model, AbstractPressureModel) - pressure_model.pressure[] = pressure_restart - else - if pressure_restart != Inf - throw(ArgumentError("Expected `Inf` for non-pressure-model boundary zones")) - end - end - end - - foreach_noalloc(boundary_zones_flow_rate, - values.boundary_zones_flow_rate) do (flow_rate, - flow_rate_restart) - flow_rate[] = flow_rate_restart - end - - return system -end +precondition_system!(system, restart_file) = system diff --git a/src/io/write_vtk.jl b/src/io/write_vtk.jl index ff2f2f85eb..14d3d0100e 100644 --- a/src/io/write_vtk.jl +++ b/src/io/write_vtk.jl @@ -419,6 +419,14 @@ function write2vtk!(vtk, v, u, t, system::OpenBoundarySystem) vtk["Q_total"] = Q_total end + if any(pm -> isa(pm, AbstractPressureModel), system.cache.pressure_reference_values) + for (i, pressure_model) in enumerate(system.cache.pressure_reference_values) + if pressure_model isa AbstractPressureModel + vtk["boundary_zone_pressure_$i"] = system.cache.pressure_reference_values[i].pressure[] + end + end + end + return vtk end diff --git a/src/schemes/boundary/open_boundary/system.jl b/src/schemes/boundary/open_boundary/system.jl index 507dcd29f5..1e5040d8c3 100644 --- a/src/schemes/boundary/open_boundary/system.jl +++ b/src/schemes/boundary/open_boundary/system.jl @@ -704,3 +704,21 @@ function restart_v(system::OpenBoundarySystem, data) return v_total end + +function precondition_system!(system::OpenBoundarySystem, file) + if any(pm -> isa(pm, AbstractPressureModel), system.cache.pressure_reference_values) + keys_p = [(Symbol(:p, i), "boundary_zone_pressure_$(i)") + for i in eachindex(system.boundary_zones)] + keys_Q = [(Symbol(:Q, i), "Q_$(i)") for i in eachindex(system.boundary_zones)] + values = vtk2trixi(file; merge(keys_p, keys_Q)...) + + for (i, pressure_model) in enumerate(system.cache.pressure_reference_values) + if pressure_model isa AbstractPressureModel + pressure_model.pressure[] = values[Symbol(:p, i)] + pressure_model.flow_rate[] = values[Symbol(:Q, i)] + end + end + end + + return system +end From 616a2dc839bfe52237bded78ecf38b4bdc64ccbd Mon Sep 17 00:00:00 2001 From: LasNikas Date: Tue, 6 Jan 2026 18:58:48 +0100 Subject: [PATCH 034/134] fix inactive particles --- src/general/restart.jl | 4 +++ src/schemes/boundary/open_boundary/system.jl | 28 +++++++++++++++----- src/schemes/fluid/fluid.jl | 7 ----- 3 files changed, 26 insertions(+), 13 deletions(-) diff --git a/src/general/restart.jl b/src/general/restart.jl index e4c85ec0c6..4c5cfdfd09 100644 --- a/src/general/restart.jl +++ b/src/general/restart.jl @@ -45,6 +45,8 @@ function set_intial_conditions!(v0_ode, u0_ode, semi, restart_conditions) end foreach_noalloc(semi.systems, restart_conditions) do (system, restart_condition) + initialize_before_restart!(system, restart_condition, semi) + v0_system = wrap_v(v0_ode, system, semi) u0_system = wrap_u(u0_ode, system, semi) @@ -86,3 +88,5 @@ function write_density_and_pressure!(v_restart, system::EntropicallyDampedSPHSys end precondition_system!(system, restart_file) = system + +initialize_before_restart!(system, restart_condition, semi) = system diff --git a/src/schemes/boundary/open_boundary/system.jl b/src/schemes/boundary/open_boundary/system.jl index 1e5040d8c3..4498c7e9dc 100644 --- a/src/schemes/boundary/open_boundary/system.jl +++ b/src/schemes/boundary/open_boundary/system.jl @@ -134,6 +134,26 @@ function initialize!(system::OpenBoundarySystem, semi) return system end +function initialize_before_restart!(system::OpenBoundarySystem, restart_condition, semi) + set_zero!(system.boundary_zone_indices) + + # We cannot simply use `update_boundary_zone_indices!` because rounding errors during file I/O + # may result in particles being located outside their intended boundary zone, even though they + # were written as active particles. + for particle in axes(restart_condition.u_restart, 2) + particle_coords = current_coords(restart_condition.u_restart, system, particle) + + for (zone_id, boundary_zone) in enumerate(system.boundary_zones) + # Check if boundary particle is in the boundary zone + if is_in_boundary_zone(boundary_zone, particle_coords) + system.boundary_zone_indices[particle] = zone_id + end + end + end + + return system +end + function create_cache_open_boundary(boundary_model, fluid_system, initial_condition, calculate_flow_rate, boundary_zones) reference_values = map(bz -> bz.reference_values, boundary_zones) @@ -265,6 +285,8 @@ end return system.shifting_technique end +@inline density_calculator(system::OpenBoundarySystem) = nothing + system_sound_speed(system::OpenBoundarySystem) = system_sound_speed(system.fluid_system) @inline hydrodynamic_mass(system::OpenBoundarySystem, particle) = system.mass[particle] @@ -683,9 +705,6 @@ end function restart_v(system::OpenBoundarySystem, data) v_total = zeros(eltype(system), v_nvariables(system), n_integrated_particles(system)) - v_total .= eltype(system)(1e16) - - system.buffer.active_particle .= false v_active = zeros(eltype(system), v_nvariables(system), size(data.velocity, 2)) @@ -694,14 +713,11 @@ function restart_v(system::OpenBoundarySystem, data) density_calculator(system), data.pressure, data.density) for particle in axes(v_active, 2) - system.buffer.active_particle[particle] = true for i in axes(v_active, 1) v_total[i, particle] = v_active[i, particle] end end - update_system_buffer!(system.buffer) - return v_total end diff --git a/src/schemes/fluid/fluid.jl b/src/schemes/fluid/fluid.jl index 22a5ad4c03..f3b93d2bb9 100644 --- a/src/schemes/fluid/fluid.jl +++ b/src/schemes/fluid/fluid.jl @@ -243,10 +243,6 @@ end function restart_v(system::AbstractFluidSystem, data) velocity_total = zeros(eltype(system), v_nvariables(system), n_integrated_particles(system)) - velocity_total .= eltype(system)(1e16) - - isnothing(buffer(system)) || (system.buffer.active_particle .= false) - velocity_active = zeros(eltype(system), v_nvariables(system), size(data.velocity, 2)) velocity_active[1:ndims(system), :] = data.velocity @@ -254,14 +250,11 @@ function restart_v(system::AbstractFluidSystem, data) data.pressure, data.density) for particle in axes(velocity_active, 2) - isnothing(buffer(system)) || (system.buffer.active_particle[particle] = true) for i in axes(velocity_active, 1) velocity_total[i, particle] = velocity_active[i, particle] end end - update_system_buffer!(system.buffer) - return velocity_total end From a80518cefa8cbd8dfc14042540a2cbe4afd51caa Mon Sep 17 00:00:00 2001 From: LasNikas Date: Tue, 6 Jan 2026 22:00:39 +0100 Subject: [PATCH 035/134] fix zone IDs --- src/general/restart.jl | 39 ++++++++++++++++++-- src/general/semidiscretization.jl | 22 ++++++++--- src/io/write_vtk.jl | 2 + src/schemes/boundary/open_boundary/system.jl | 34 ++++++----------- src/schemes/fluid/fluid.jl | 7 +++- 5 files changed, 71 insertions(+), 33 deletions(-) diff --git a/src/general/restart.jl b/src/general/restart.jl index 4c5cfdfd09..b5a6d5717e 100644 --- a/src/general/restart.jl +++ b/src/general/restart.jl @@ -45,8 +45,6 @@ function set_intial_conditions!(v0_ode, u0_ode, semi, restart_conditions) end foreach_noalloc(semi.systems, restart_conditions) do (system, restart_condition) - initialize_before_restart!(system, restart_condition, semi) - v0_system = wrap_v(v0_ode, system, semi) u0_system = wrap_u(u0_ode, system, semi) @@ -89,4 +87,39 @@ end precondition_system!(system, restart_file) = system -initialize_before_restart!(system, restart_condition, semi) = system +function initialize_neighborhood_searches!(semi, u0_ode, restart_conditions) + foreach_system(semi) do system + foreach_system(semi) do neighbor + # TODO Initialize after adapting to the GPU. + # Currently, this cannot use `semi.parallelization_backend` + # because data is still on the CPU. + PointNeighbors.initialize!(get_neighborhood_search(system, neighbor, semi), + initial_restart_coordinates(system, u0_ode, semi), + initial_restart_coordinates(neighbor, u0_ode, semi), + eachindex_y=each_active_particle(neighbor), + parallelization_backend=PolyesterBackend()) + end + end + + return semi +end + +function initial_restart_coordinates(system, u0_ode, semi) + return wrap_u(u0_ode, system, semi) +end + +function initial_restart_coordinates(system::Union{WallBoundarySystem, + AbstractStructureSystem}, u0_ode, semi) + return initial_coordinates(system) +end + +function initialize!(semi::Semidiscretization, restart_conditions) + foreach_system(semi) do system + # Initialize this system + initialize_restart!(system, semi) + end + + return semi +end + +initialize_restart!(system, semi) = initialize!(system, semi) diff --git a/src/general/semidiscretization.jl b/src/general/semidiscretization.jl index dc991aae95..71531aa402 100644 --- a/src/general/semidiscretization.jl +++ b/src/general/semidiscretization.jl @@ -333,7 +333,7 @@ function semidiscretize(semi, tspan; reset_threads=true, restart_conditions=noth # TODO initialize after adapting to the GPU. # Requires https://github.com/trixi-framework/PointNeighbors.jl/pull/86. - initialize_neighborhood_searches!(semi) + initialize_neighborhood_searches!(semi, u0_ode, restart_conditions) if semi.parallelization_backend isa KernelAbstractions.Backend # Convert all arrays to the correct array type. @@ -357,10 +357,7 @@ function semidiscretize(semi, tspan; reset_threads=true, restart_conditions=noth end # Initialize all particle systems - foreach_system(semi_new) do system - # Initialize this system - initialize!(system, semi_new) - end + initialize!(semi_new, restart_conditions) # Reset callback flag that will be set by the `UpdateCallback` semi_new.update_callback_used[] = false @@ -381,6 +378,15 @@ end time_span(tspan, restart_conditions::Nothing) = tspan +function initialize!(semi::Semidiscretization, restart_conditions::Nothing) + foreach_system(semi) do system + # Initialize this system + initialize!(system, semi) + end + + return semi +end + """ restart_with!(semi, sol) @@ -416,6 +422,10 @@ function restart_with!(semi, sol; reset_threads=true) return semi end +function initialize_neighborhood_searches!(semi, u0_ode, restart_conditions::Nothing) + initialize_neighborhood_searches!(semi) +end + function initialize_neighborhood_searches!(semi) foreach_system(semi) do system foreach_system(semi) do neighbor @@ -1112,7 +1122,7 @@ function check_configuration(system::ImplicitIncompressibleSPHSystem, systems, n neighbor.boundary_model.density_calculator isa PressureBoundaries) time_step_boundary = neighbor.boundary_model.density_calculator.time_step omega_boundary = neighbor.boundary_model.density_calculator.omega - if !(time_step==time_step_boundary && omega==omega_boundary) + if !(time_step == time_step_boundary && omega == omega_boundary) throw(ArgumentError("`PressureBoundaries` parameters have to be the same as the `ImplicitIncompressibleSPHSystem` parameters")) end diff --git a/src/io/write_vtk.jl b/src/io/write_vtk.jl index 14d3d0100e..74b99b5c36 100644 --- a/src/io/write_vtk.jl +++ b/src/io/write_vtk.jl @@ -408,6 +408,8 @@ function write2vtk!(vtk, v, u, t, system::OpenBoundarySystem) for particle in eachparticle(system)] vtk["pressure"] = [current_pressure(v, system, particle) for particle in eachparticle(system)] + vtk["zone_id"] = [system.boundary_zone_indices[particle] + for particle in eachparticle(system)] if system.cache.calculate_flow_rate Q_total = zero(eltype(system)) diff --git a/src/schemes/boundary/open_boundary/system.jl b/src/schemes/boundary/open_boundary/system.jl index 4498c7e9dc..6270999880 100644 --- a/src/schemes/boundary/open_boundary/system.jl +++ b/src/schemes/boundary/open_boundary/system.jl @@ -134,25 +134,8 @@ function initialize!(system::OpenBoundarySystem, semi) return system end -function initialize_before_restart!(system::OpenBoundarySystem, restart_condition, semi) - set_zero!(system.boundary_zone_indices) - - # We cannot simply use `update_boundary_zone_indices!` because rounding errors during file I/O - # may result in particles being located outside their intended boundary zone, even though they - # were written as active particles. - for particle in axes(restart_condition.u_restart, 2) - particle_coords = current_coords(restart_condition.u_restart, system, particle) - - for (zone_id, boundary_zone) in enumerate(system.boundary_zones) - # Check if boundary particle is in the boundary zone - if is_in_boundary_zone(boundary_zone, particle_coords) - system.boundary_zone_indices[particle] = zone_id - end - end - end - - return system -end +# Skip during restart, as boundary zone indices are updated in `precondition_system!` +initialize_restart!(system::OpenBoundarySystem, semi) = system function create_cache_open_boundary(boundary_model, fluid_system, initial_condition, calculate_flow_rate, boundary_zones) @@ -686,17 +669,17 @@ function restart_u(system::OpenBoundarySystem, data) coords_total = zeros(eltype(system), u_nvariables(system), n_integrated_particles(system)) coords_total .= eltype(system)(1e16) - system.buffer.active_particle .= false coords_active = data.coordinates - for particle in axes(coords_active, 2) - system.buffer.active_particle[particle] = true for dim in 1:ndims(system) coords_total[dim, particle] = coords_active[dim, particle] end end + system.buffer.active_particle .= false + system.buffer.active_particle[1:size(coords_active, 2)] .= true + update_system_buffer!(system.buffer) return coords_total @@ -722,6 +705,13 @@ function restart_v(system::OpenBoundarySystem, data) end function precondition_system!(system::OpenBoundarySystem, file) + # We cannot simply use `update_boundary_zone_indices!` because rounding errors during file I/O + # may result in particles being located outside their intended boundary zone, even though they + # were written as active particles. + set_zero!(system.boundary_zone_indices) + values = vtk2trixi(file; zone_id="zone_id") + system.boundary_zone_indices[each_integrated_particle(system)] .= values.zone_id + if any(pm -> isa(pm, AbstractPressureModel), system.cache.pressure_reference_values) keys_p = [(Symbol(:p, i), "boundary_zone_pressure_$(i)") for i in eachindex(system.boundary_zones)] diff --git a/src/schemes/fluid/fluid.jl b/src/schemes/fluid/fluid.jl index f3b93d2bb9..fbdef99f1b 100644 --- a/src/schemes/fluid/fluid.jl +++ b/src/schemes/fluid/fluid.jl @@ -224,17 +224,20 @@ function restart_u(system::AbstractFluidSystem, data) coords_total = zeros(eltype(system), u_nvariables(system), n_integrated_particles(system)) coords_total .= eltype(system)(1e16) - isnothing(buffer(system)) || (system.buffer.active_particle .= false) coords_active = data.coordinates for particle in axes(coords_active, 2) - isnothing(buffer(system)) || (system.buffer.active_particle[particle] = true) for dim in 1:ndims(system) coords_total[dim, particle] = coords_active[dim, particle] end end + if !isnothing(buffer(system)) + system.buffer.active_particle .= false + system.buffer.active_particle[1:size(coords_active, 2)] .= true + end + update_system_buffer!(system.buffer) return coords_total From 439cb6b9e0d73c12045faf72da970297fe2a1b77 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Tue, 6 Jan 2026 22:02:07 +0100 Subject: [PATCH 036/134] add test --- test/general/general.jl | 1 + test/general/restart.jl | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 test/general/restart.jl diff --git a/test/general/general.jl b/test/general/general.jl index 5a75b9078f..e194d8350e 100644 --- a/test/general/general.jl +++ b/test/general/general.jl @@ -6,3 +6,4 @@ include("interpolation.jl") include("buffer.jl") include("util.jl") include("custom_quantities.jl") +include("restart.jl") diff --git a/test/general/restart.jl b/test/general/restart.jl new file mode 100644 index 0000000000..c2ddc609c9 --- /dev/null +++ b/test/general/restart.jl @@ -0,0 +1,41 @@ +@trixi_testset "Restart" begin + # Run full simulation + trixi_include(@__MODULE__, + joinpath(examples_dir(), "fluid", "poiseuille_flow_2d.jl"), + tspan=(0.0, 0.6), sound_speed_factor=10, particle_spacing=4e-5) + + # Since this is an open boundary simulation, the number of active particles may + # differ. The results must be interpolated to enable comparison with the restart + # simulation. The fluid domain starts at `x = 10 * particle_spacing`. + n_interpolation_points = 10 + start_point = [0.0 + 10 * particle_spacing, wall_distance / 2] + end_point = [flow_length - 10 * particle_spacing, wall_distance / 2] + result_full = interpolate_line(start_point, end_point, n_interpolation_points, + semi, fluid_system, sol, cut_off_bnd=false) + + # Run half simulation + trixi_include(@__MODULE__, + joinpath(examples_dir(), "fluid", "poiseuille_flow_2d.jl"), + tspan=(0.0, 0.3), sound_speed_factor=10, particle_spacing=4e-5) + + iter = round(Int, 0.3 / 0.02) + fluid_restart = RestartCondition(fluid_system, "fluid_1_$iter.vtu") + open_boundary_restart = RestartCondition(open_boundary, "open_boundary_1_$iter.vtu") + boundary_restart = RestartCondition(boundary_system, "boundary_1_$iter.vtu") + + ode_restart = semidiscretize(semi, (0.3, 0.6); + restart_conditions=(fluid_restart, open_boundary_restart, + boundary_restart)) + + sol_restart = solve(ode_restart, RDPK3SpFSAL35(), abstol=1e-5, reltol=1e-3, dtmax=1e-2, + save_everystep=false, callback=UpdateCallback()) + + result_restart = interpolate_line(start_point, end_point, + n_interpolation_points, sol_restart.prob.p, + sol_restart.prob.p.systems[1], + sol_restart, cut_off_bnd=false) + + @test isapprox(result_full.velocity, result_restart.velocity, rtol=8e-3) + @test isapprox(result_full.density, result_restart.density, rtol=8e-4) + @test isapprox(result_full.pressure, result_restart.pressure, rtol=7e-2) +end From f3049a479e6e389288bad69abe2a83873c494cb8 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Tue, 6 Jan 2026 22:47:19 +0100 Subject: [PATCH 037/134] make it gpu compatible --- src/general/restart.jl | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/general/restart.jl b/src/general/restart.jl index b5a6d5717e..abf8af3bc9 100644 --- a/src/general/restart.jl +++ b/src/general/restart.jl @@ -16,7 +16,9 @@ function RestartCondition(system::AbstractSystem, filename; output_directory="ou precondition_system!(system, restart_file) - return RestartCondition(v_restart, u_restart, restart_data.time) + t_restart = convert(eltype(system), restart_data.time) + + return RestartCondition(v_restart, u_restart, t_restart) end function Base.show(io::IO, rc::RestartCondition) @@ -48,8 +50,8 @@ function set_intial_conditions!(v0_ode, u0_ode, semi, restart_conditions) v0_system = wrap_v(v0_ode, system, semi) u0_system = wrap_u(u0_ode, system, semi) - u0_system .= restart_condition.u_restart - v0_system .= restart_condition.v_restart + v0_system .= Adapt.adapt(semi.parallelization_backend, restart_condition.v_restart) + u0_system .= Adapt.adapt(semi.parallelization_backend, restart_condition.u_restart) end end @@ -105,7 +107,8 @@ function initialize_neighborhood_searches!(semi, u0_ode, restart_conditions) end function initial_restart_coordinates(system, u0_ode, semi) - return wrap_u(u0_ode, system, semi) + # Transfer to CPU if data is on the GPU. Do nothing if already on CPU. + return transfer2cpu(wrap_u(u0_ode, system, semi)) end function initial_restart_coordinates(system::Union{WallBoundarySystem, From 1c7cd3810b7abac24bb2f6d6bf34bed4aca189f0 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Tue, 6 Jan 2026 22:50:00 +0100 Subject: [PATCH 038/134] add gpu tests --- test/examples/gpu.jl | 56 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/test/examples/gpu.jl b/test/examples/gpu.jl index 0e2655437c..4672bae45c 100644 --- a/test/examples/gpu.jl +++ b/test/examples/gpu.jl @@ -645,4 +645,60 @@ end end end end + + @testset verbose=true "Restart" begin + trixi_include_changeprecision(Float32, @__MODULE__, + joinpath(examples_dir(), "fluid", + "poiseuille_flow_2d.jl"), + tspan=(0.0f0, 0.6f0), sound_speed_factor=10, + particle_spacing=4.0f-5, + coordinates_eltype=Float32, + parallelization_backend=Main.parallelization_backend) + + # Since this is an open boundary simulation, the number of active particles may + # differ. The results must be interpolated to enable comparison with the restart + # simulation. The fluid domain starts at `x = 10 * particle_spacing`. + n_interpolation_points = 10 + start_point = [0.0f0 + 10 * particle_spacing, wall_distance / 2] + end_point = [flow_length - 10 * particle_spacing, wall_distance / 2] + result_full = interpolate_line(start_point, end_point, n_interpolation_points, + sol.prob.p, sol.prob.p.systems[1], sol, + cut_off_bnd=false) + + # Run half simulation and safe checkpoint + trixi_include_changeprecision(Float32, @__MODULE__, + joinpath(examples_dir(), "fluid", + "poiseuille_flow_2d.jl"), + tspan=(0.0f0, 0.3f0), sound_speed_factor=10, + particle_spacing=4.0f-5, + coordinates_eltype=Float32, + parallelization_backend=Main.parallelization_backend) + + # Transfer `semi` back to CPU + semi_new = TrixiParticles.Adapt.adapt(Array, sol.prob.p) + + iter = round(Int, 0.3 / 0.02) + fluid_restart = RestartCondition(semi_new.systems[1], "fluid_1_$iter.vtu") + open_boundary_restart = RestartCondition(semi_new.systems[2], + "open_boundary_1_$iter.vtu") + boundary_restart = RestartCondition(semi_new.systems[3], "boundary_1_$iter.vtu") + + ode_restart = semidiscretize(semi_new, (0.3f0, 0.6f0); + restart_conditions=(fluid_restart, + open_boundary_restart, + boundary_restart)) + + sol_restart = solve(ode_restart, RDPK3SpFSAL35(), abstol=1.0f-5, + reltol=1.0f-3, dtmax=1.0f-2, save_everystep=false, + callback=UpdateCallback()) + + result_restart = interpolate_line(start_point, end_point, + n_interpolation_points, sol_restart.prob.p, + sol_restart.prob.p.systems[1], + sol_restart, cut_off_bnd=false) + + @test isapprox(result_full.velocity, result_restart.velocity, rtol=8e-3) + @test isapprox(result_full.density, result_restart.density, rtol=8e-4) + @test isapprox(result_full.pressure, result_restart.pressure, rtol=8e-2) + end end From c605502510426561473106939e85d242e7045798 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Wed, 7 Jan 2026 08:44:20 +0100 Subject: [PATCH 039/134] fix tests --- src/io/read_vtk.jl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/io/read_vtk.jl b/src/io/read_vtk.jl index 6f0701f560..f4f4f61781 100644 --- a/src/io/read_vtk.jl +++ b/src/io/read_vtk.jl @@ -64,7 +64,10 @@ function vtk2trixi(file; element_type=:default, coordinates_eltype=Float64, if idx !== nothing results[field] = convert.(ELTYPE, ReadVTK.get_data(point_data[all_keys[idx]])) else - @info "No '$field' field found in VTK file" + # Use zeros as default values when a field is missing + results[field] = string(field) in ["velocity"] ? + zero(coordinates) : zeros(ELTYPE, size(coordinates, 2)) + @info "No '$field' field found in VTK file. Will be set to zero." end end From 1cada7704c4028c3e78b36e0f4354d84d1ac331b Mon Sep 17 00:00:00 2001 From: LasNikas Date: Wed, 7 Jan 2026 09:24:23 +0100 Subject: [PATCH 040/134] add checks --- src/general/restart.jl | 25 +++++++++++++++----- src/schemes/boundary/open_boundary/system.jl | 4 ++-- src/schemes/fluid/fluid.jl | 4 ++-- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/general/restart.jl b/src/general/restart.jl index abf8af3bc9..24e387313a 100644 --- a/src/general/restart.jl +++ b/src/general/restart.jl @@ -1,7 +1,8 @@ struct RestartCondition{V, U} - v_restart::V - u_restart::U - t_restart::Real + system :: AbstractSystem + v_restart :: V + u_restart :: U + t_restart :: Real end function RestartCondition(system::AbstractSystem, filename; output_directory="out") @@ -18,13 +19,13 @@ function RestartCondition(system::AbstractSystem, filename; output_directory="ou t_restart = convert(eltype(system), restart_data.time) - return RestartCondition(v_restart, u_restart, t_restart) + return RestartCondition(system, v_restart, u_restart, t_restart) end function Base.show(io::IO, rc::RestartCondition) @nospecialize rc # reduce precompilation time - print(io, "RestartCondition{}()") + print(io, "RestartCondition{$(nameof(typeof(rc.system)))}()") end function Base.show(io::IO, ::MIME"text/plain", rc::RestartCondition) @@ -34,18 +35,30 @@ function Base.show(io::IO, ::MIME"text/plain", rc::RestartCondition) show(io, rc) else summary_header(io, "RestartCondition") + summary_line(io, "System", "$(nameof(typeof(rc.system)))") summary_line(io, "#particles u", "$(size(rc.u_restart, 2))") summary_line(io, "#particles v", "$(size(rc.v_restart, 2))") - summary_line(io, "eltype", "$(eltype(rc.v_restart))") + summary_line(io, "eltype u", "$(eltype(rc.u_restart))") + summary_line(io, "eltype v", "$(eltype(rc.v_restart))") summary_footer(io) end end function set_intial_conditions!(v0_ode, u0_ode, semi, restart_conditions) + # Check number of systems if length(semi.systems) != length(restart_conditions) throw(ArgumentError("Number of systems in `semi` does not match number of `restart_conditions`")) end + # Check that systems match + foreach_system(semi) do system + system_index = system_indices(system, semi) + if !(system == restart_conditions[system_index].system) + throw(ArgumentError("System at index $system_index in `semi` does not match system in `restart_conditions`")) + end + end + + # Set initial conditions foreach_noalloc(semi.systems, restart_conditions) do (system, restart_condition) v0_system = wrap_v(v0_ode, system, semi) u0_system = wrap_u(u0_ode, system, semi) diff --git a/src/schemes/boundary/open_boundary/system.jl b/src/schemes/boundary/open_boundary/system.jl index 6270999880..3524e1cfe6 100644 --- a/src/schemes/boundary/open_boundary/system.jl +++ b/src/schemes/boundary/open_boundary/system.jl @@ -666,9 +666,9 @@ function interpolate_velocity!(system::OpenBoundarySystem, boundary_zone, end function restart_u(system::OpenBoundarySystem, data) - coords_total = zeros(eltype(system), u_nvariables(system), + coords_total = zeros(coordinates_eltype(system), u_nvariables(system), n_integrated_particles(system)) - coords_total .= eltype(system)(1e16) + coords_total .= coordinates_eltype(system)(1e16) coords_active = data.coordinates for particle in axes(coords_active, 2) diff --git a/src/schemes/fluid/fluid.jl b/src/schemes/fluid/fluid.jl index fbdef99f1b..a9fd06f4e6 100644 --- a/src/schemes/fluid/fluid.jl +++ b/src/schemes/fluid/fluid.jl @@ -221,9 +221,9 @@ end end function restart_u(system::AbstractFluidSystem, data) - coords_total = zeros(eltype(system), u_nvariables(system), + coords_total = zeros(coordinates_eltype(system), u_nvariables(system), n_integrated_particles(system)) - coords_total .= eltype(system)(1e16) + coords_total .= coordinates_eltype(system)(1e16) coords_active = data.coordinates From 100590e5e77097c0ec0618c92341b20659e4f150 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Wed, 7 Jan 2026 09:26:44 +0100 Subject: [PATCH 041/134] add example --- .../restart_poiseuille_flow_2d.jl | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 examples/postprocessing/restart_poiseuille_flow_2d.jl diff --git a/examples/postprocessing/restart_poiseuille_flow_2d.jl b/examples/postprocessing/restart_poiseuille_flow_2d.jl new file mode 100644 index 0000000000..ff3701f972 --- /dev/null +++ b/examples/postprocessing/restart_poiseuille_flow_2d.jl @@ -0,0 +1,32 @@ +# ========================================================================================== +# Restart Example +# +# TODO +# ========================================================================================== +using TrixiParticles + +# Run full simulation +# trixi_include(@__MODULE__, +# joinpath(examples_dir(), "fluid", "poiseuille_flow_2d.jl"), +# tspan=(0.0, 0.6), sound_speed_factor=10, particle_spacing=4e-5) + +# Run half simulation +trixi_include(@__MODULE__, + joinpath(examples_dir(), "fluid", "poiseuille_flow_2d.jl"), + tspan=(0.0, 0.3), sound_speed_factor=10, particle_spacing=4e-5) + +iter = round(Int, 0.3 / 0.02) +fluid_restart = RestartCondition(fluid_system, "fluid_1_$iter.vtu") +open_boundary_restart = RestartCondition(open_boundary, "open_boundary_1_$iter.vtu") +boundary_restart = RestartCondition(boundary_system, "boundary_1_$iter.vtu") + +ode_restart = semidiscretize(semi, (0.3, 0.6); + restart_conditions=(fluid_restart, open_boundary_restart, + boundary_restart)) + +saving_callback = SolutionSavingCallback(dt=0.02, prefix="restart") + +callbacks = CallbackSet(info_callback, saving_callback, UpdateCallback()) + +sol_restart = solve(ode_restart, RDPK3SpFSAL35(), abstol=1e-5, reltol=1e-3, dtmax=1e-2, + save_everystep=false, callback=callbacks) From 358848df8793a9b043875b0299b6c566588673f6 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Wed, 7 Jan 2026 09:35:25 +0100 Subject: [PATCH 042/134] add docs --- src/general/restart.jl | 13 +++++++++++++ src/general/semidiscretization.jl | 5 ++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/general/restart.jl b/src/general/restart.jl index 24e387313a..e49cd9b873 100644 --- a/src/general/restart.jl +++ b/src/general/restart.jl @@ -1,3 +1,16 @@ +""" + RestartCondition(system::AbstractSystem, filename; output_directory="out") + +Create a `RestartCondition` for the given `system` from the specified `filename`. + +# Arguments +- `system`: The system to restart. +- `filename`: The name of the file from which to restart. This file should be in + VTK format and correspond to the specified `system`. + +# Keywords +- `output_directory`: The directory where the restart file is located (default: `"out"`). +""" struct RestartCondition{V, U} system :: AbstractSystem v_restart :: V diff --git a/src/general/semidiscretization.jl b/src/general/semidiscretization.jl index 71531aa402..163f0fc77f 100644 --- a/src/general/semidiscretization.jl +++ b/src/general/semidiscretization.jl @@ -253,7 +253,7 @@ end @inline foreach_system(f, systems) = foreach_noalloc(f, systems) """ - semidiscretize(semi, tspan; reset_threads=true) + semidiscretize(semi, tspan; reset_threads=true, restart_conditions=nothing) Create an `ODEProblem` from the semidiscretization with the specified `tspan`. @@ -262,6 +262,9 @@ Create an `ODEProblem` from the semidiscretization with the specified `tspan`. - `tspan`: The time span over which the simulation will be run. # Keywords +- `restart_conditions`: A tuple of [`RestartCondition`](@ref) objects specifying + from which files to restart each system. The order has to match the order of systems + in `semi`. By default, no restart is performed. - `reset_threads`: A boolean flag to reset Polyester.jl threads before the simulation (default: `true`). After an error within a threaded loop, threading might be disabled. Resetting the threads before the simulation ensures that threading is enabled again for the simulation. From 2b56dc8dc8e4e486e1632c4e5fdeaf8bccb761d5 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Wed, 7 Jan 2026 09:37:58 +0100 Subject: [PATCH 043/134] fix typo --- src/general/restart.jl | 2 +- src/general/semidiscretization.jl | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/general/restart.jl b/src/general/restart.jl index e49cd9b873..75de2d8822 100644 --- a/src/general/restart.jl +++ b/src/general/restart.jl @@ -57,7 +57,7 @@ function Base.show(io::IO, ::MIME"text/plain", rc::RestartCondition) end end -function set_intial_conditions!(v0_ode, u0_ode, semi, restart_conditions) +function set_initial_conditions!(v0_ode, u0_ode, semi, restart_conditions) # Check number of systems if length(semi.systems) != length(restart_conditions) throw(ArgumentError("Number of systems in `semi` does not match number of `restart_conditions`")) diff --git a/src/general/semidiscretization.jl b/src/general/semidiscretization.jl index 163f0fc77f..35bcf55a29 100644 --- a/src/general/semidiscretization.jl +++ b/src/general/semidiscretization.jl @@ -332,7 +332,7 @@ function semidiscretize(semi, tspan; reset_threads=true, restart_conditions=noth end # Set initial condition - set_intial_conditions!(v0_ode, u0_ode, semi, restart_conditions) + set_initial_conditions!(v0_ode, u0_ode, semi, restart_conditions) # TODO initialize after adapting to the GPU. # Requires https://github.com/trixi-framework/PointNeighbors.jl/pull/86. @@ -369,7 +369,7 @@ function semidiscretize(semi, tspan; reset_threads=true, restart_conditions=noth time_span(tspan, restart_conditions), semi_new) end -function set_intial_conditions!(v0_ode, u0_ode, semi, restart_conditions::Nothing) +function set_initial_conditions!(v0_ode, u0_ode, semi, restart_conditions::Nothing) foreach_system(semi) do system v0_system = wrap_v(v0_ode, system, semi) u0_system = wrap_u(u0_ode, system, semi) From 7c087617d516094258948c276cbd6edd8f16d0b8 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Wed, 7 Jan 2026 12:11:52 +0100 Subject: [PATCH 044/134] implement suggestions --- .../boundary/open_boundary/boundary_zones.jl | 34 +++++++++++-------- src/schemes/boundary/open_boundary/system.jl | 16 ++++----- 2 files changed, 27 insertions(+), 23 deletions(-) diff --git a/src/schemes/boundary/open_boundary/boundary_zones.jl b/src/schemes/boundary/open_boundary/boundary_zones.jl index 8e42b7088a..75fab302cc 100644 --- a/src/schemes/boundary/open_boundary/boundary_zones.jl +++ b/src/schemes/boundary/open_boundary/boundary_zones.jl @@ -79,14 +79,20 @@ There are three ways to specify the actual shape of the boundary zone: - `rest_pressure=0.0`: For `BoundaryModelDynamicalPressureZhang`, a rest pressure is required when the pressure is not prescribed. This should match the rest pressure of the fluid system. Per default it is set to zero (assuming a gauge pressure system). - - For `EntropicallyDampedSPHSystem`: Use the initial pressure from the `InitialCondition` - - For `WeaklyCompressibleSPHSystem`: Use the background pressure from the equation of state -- `sample_points=:default`: Either `:default` to automatically generate sample points on the boundary face (default), - or a matrix of dimensions `(ndims, npoints)` containing sample points - on the boundary face used to compute the volumetric flow rate. - Each sample point represents a discrete area of `particle_spacing^(ndims-1)`. - Therefore, `sample_points` must form a regular grid. - Set to `nothing` to skip sampling. + - For `EntropicallyDampedSPHSystem`: Use the initial pressure from the `InitialCondition` + - For `WeaklyCompressibleSPHSystem`: Use the background pressure from the equation of state +- `sample_points=:default`: Controls how the boundary face is sampled to estimate the + volumetric flow rate. + - `:default` (default): Automatically generate sampling points + on the boundary face using a regular grid with spacing `particle_spacing`. + - `sample_points::AbstractMatrix`: Provide your own sampling points + as a matrix of size `(ndims, npoints)`, where **each column is + one point** on the boundary face (line in 2D, rectangle/face in 3D). + The points must lie on the face and form a regular grid. + - `nothing`: Disable flow-rate sampling. + Note: Each sampling point represents an area element of size + `particle_spacing^(ndims-1)`. Therefore, the discretized + sampled area is `npoints * particle_spacing^(ndims-1)`. !!! note "Note" The reference values (`reference_velocity`, `reference_pressure`, `reference_density`) @@ -330,9 +336,9 @@ function create_cache_boundary_zone(initial_condition, boundary_face, face_norma direction=(-face_normal), n_extrude=1).coordinates sample_points_ = convert.(eltype(initial_condition), points) else - if !(sample_points isa Matrix && size(sample_points, 1) == ndims(initial_condition)) - throw(ArgumentError("`sample_points` must be a matrix with " * - "`ndims(initial_condition)` rows")) + if !(sample_points isa AbstractMatrix && + size(sample_points, 1) == ndims(initial_condition)) + throw(ArgumentError("`sample_points` must be an `(ndims, npoints)` matrix with $(ndims(initial_condition)) rows")) end sample_points_ = convert.(eltype(initial_condition), sample_points) end @@ -347,9 +353,9 @@ function create_cache_boundary_zone(initial_condition, boundary_face, face_norma face_area = norm(v2 - v1) end - # We only check if the discretized area exceeds the boundary face area. - # For 3D boundary zones with complex or non-rectangular flow profiles - # (e.g., pipe flow), the cross-sectional area can legitimately be smaller than the boundary face area. + # We only warn when the discretized sampled area is larger than the geometric face area. + # In 3D, the effective flow cross-section may be smaller than the boundary face, especially for + # complex or non-rectangular profiles (e.g. pipe flow), so smaller sampled areas are acceptable. if discrete_face_area > (face_area + eps(face_area)) @warn "The sampled area of the boundary face " * "($(discrete_face_area)) is larger than the actual face area ($(face_area)). " diff --git a/src/schemes/boundary/open_boundary/system.jl b/src/schemes/boundary/open_boundary/system.jl index 0e09c669c0..08f35b263d 100644 --- a/src/schemes/boundary/open_boundary/system.jl +++ b/src/schemes/boundary/open_boundary/system.jl @@ -355,12 +355,8 @@ function calculate_flow_rate!(system::OpenBoundarySystem{<:Any, ELTYPE, NDIMS}, # Compute volumetric flow rate: Q = ∫ v ⋅ n dA velocities = reinterpret(reshape, SVector{NDIMS, ELTYPE}, sample_velocity) - current_flow_rate = sum(velocities) do velocity - vn = dot(velocity, -face_normal) - return vn * area_increment - end - - flow_rate[] = current_flow_rate + flow_rate[] = @inbounds area_increment * + sum(v -> dot(v, -face_normal), velocities) end end @@ -624,6 +620,8 @@ function interpolate_velocity!(system::OpenBoundarySystem, boundary_zone, set_zero!(shepard_coefficient) set_zero!(sample_velocity) + # Shepard-normalized interpolation: + # v(p) = (Σ_b v_b V_b W_pb) / (Σ_b V_b W_pb) foreach_system(semi) do neighbor_system v_neighbor = wrap_v(v_ode, neighbor_system, semi) u_neighbor = wrap_u(u_ode, neighbor_system, semi) @@ -639,15 +637,15 @@ function interpolate_velocity!(system::OpenBoundarySystem, boundary_zone, shepard_coefficient[point] += volume_b * W_ab velocity_neighbor = viscous_velocity(v_neighbor, neighbor_system, neighbor) - for i in axes(velocity_neighbor, 1) + @inbounds for i in axes(velocity_neighbor, 1) sample_velocity[i, point] += velocity_neighbor[i] * volume_b * W_ab end end end @threaded semi for point in axes(sample_points, 2) - if shepard_coefficient[point] > eps() - for i in axes(sample_velocity, 1) + if shepard_coefficient[point] > eps(eltype(shepard_coefficient)) + @inbounds for i in axes(sample_velocity, 1) sample_velocity[i, point] /= shepard_coefficient[point] end end From 4a6bca19fe3e61bc8d030d88ba115a8beb819abb Mon Sep 17 00:00:00 2001 From: LasNikas Date: Wed, 7 Jan 2026 12:28:50 +0100 Subject: [PATCH 045/134] implement suggestions --- src/io/read_vtk.jl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/io/read_vtk.jl b/src/io/read_vtk.jl index 0638c5c21a..37f5cd7d0c 100644 --- a/src/io/read_vtk.jl +++ b/src/io/read_vtk.jl @@ -7,8 +7,9 @@ Load VTK file and convert data to an [`InitialCondition`](@ref). - `file`: Name of the VTK file to be loaded. # Keywords -- `element_type`: Element type for particle fields (`:default` keeps the type - stored in the VTK file, otherwise converted to the given type). +- `element_type`: Element type for particle fields. By default, the type + stored in the VTK file is used. + Otherwise, data is converted to the specified type. - `coordinates_eltype`: Element type for particle coordinates (defaults to `Float64`). !!! warning "Experimental Implementation" From f8f788c5bfcc3af1d0722cb990e0f512a7e5a852 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Wed, 7 Jan 2026 14:33:53 +0100 Subject: [PATCH 046/134] implement suggestions --- src/io/read_vtk.jl | 12 +++++++----- test/io/read_vtk.jl | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/io/read_vtk.jl b/src/io/read_vtk.jl index 37f5cd7d0c..15618afa72 100644 --- a/src/io/read_vtk.jl +++ b/src/io/read_vtk.jl @@ -1,5 +1,5 @@ """ - vtk2trixi(file::String; element_type=:default, coordinates_eltype=Float64) + vtk2trixi(file::String; element_type=nothing, coordinates_eltype=nothing) Load VTK file and convert data to an [`InitialCondition`](@ref). @@ -10,7 +10,9 @@ Load VTK file and convert data to an [`InitialCondition`](@ref). - `element_type`: Element type for particle fields. By default, the type stored in the VTK file is used. Otherwise, data is converted to the specified type. -- `coordinates_eltype`: Element type for particle coordinates (defaults to `Float64`). +- `coordinates_eltype`: Element type for particle coordinates. By default, the type + stored in the VTK file is used. + Otherwise, data is converted to the specified type. !!! warning "Experimental Implementation" This is an experimental feature and may change in any future releases. @@ -39,7 +41,7 @@ ic = vtk2trixi(joinpath("out", "rectangular.vtu")) └──────────────────────────────────────────────────────────────────────────────────────────────────┘ ``` """ -function vtk2trixi(file; element_type=:default, coordinates_eltype=Float64) +function vtk2trixi(file; element_type=nothing, coordinates_eltype=nothing) vtk_file = ReadVTK.VTKFile(file) # Retrieve data fields (e.g., pressure, velocity, ...) @@ -47,8 +49,8 @@ function vtk2trixi(file; element_type=:default, coordinates_eltype=Float64) field_data = ReadVTK.get_field_data(vtk_file) point_coords = ReadVTK.get_points(vtk_file) - cELTYPE = coordinates_eltype - ELTYPE = element_type === :default ? eltype(point_coords) : element_type + cELTYPE = isnothing(coordinates_eltype) ? eltype(point_coords) : coordinates_eltype + ELTYPE = isnothing(element_type) ? eltype(point_coords) : element_type # Retrieve fields ndims = first(ReadVTK.get_data(field_data["ndims"])) diff --git a/test/io/read_vtk.jl b/test/io/read_vtk.jl index 1331fdc9b9..cdb350931b 100644 --- a/test/io/read_vtk.jl +++ b/test/io/read_vtk.jl @@ -38,7 +38,7 @@ @test isapprox(expected_ic_32.density, test_ic.density, rtol=1e-5) @test isapprox(expected_ic_32.pressure, test_ic.pressure, rtol=1e-5) @test eltype(test_ic) === Float32 - @test eltype(test_ic.coordinates) === Float64 + @test eltype(test_ic.coordinates) === Float32 end @testset verbose=true "Custom Element Type" begin From 9e4276adc8a1e5a13544cd71ea147a4f5b79b9bf Mon Sep 17 00:00:00 2001 From: LasNikas Date: Wed, 7 Jan 2026 15:30:49 +0100 Subject: [PATCH 047/134] use strings instead of RestartConditin --- .../restart_poiseuille_flow_2d.jl | 12 ++- src/TrixiParticles.jl | 2 +- src/general/restart.jl | 96 +++++-------------- src/general/semidiscretization.jl | 27 +++--- test/examples/gpu.jl | 12 +-- test/general/restart.jl | 10 +- 6 files changed, 57 insertions(+), 102 deletions(-) diff --git a/examples/postprocessing/restart_poiseuille_flow_2d.jl b/examples/postprocessing/restart_poiseuille_flow_2d.jl index ff3701f972..fdde06d4b4 100644 --- a/examples/postprocessing/restart_poiseuille_flow_2d.jl +++ b/examples/postprocessing/restart_poiseuille_flow_2d.jl @@ -16,13 +16,15 @@ trixi_include(@__MODULE__, tspan=(0.0, 0.3), sound_speed_factor=10, particle_spacing=4e-5) iter = round(Int, 0.3 / 0.02) -fluid_restart = RestartCondition(fluid_system, "fluid_1_$iter.vtu") -open_boundary_restart = RestartCondition(open_boundary, "open_boundary_1_$iter.vtu") -boundary_restart = RestartCondition(boundary_system, "boundary_1_$iter.vtu") + +restart_file_fluid = joinpath("out", "fluid_1_$iter.vtu") +restart_file_open_boundary = joinpath("out", "open_boundary_1_$iter.vtu") +restart_file_boundary = joinpath("out", "boundary_1_$iter.vtu") ode_restart = semidiscretize(semi, (0.3, 0.6); - restart_conditions=(fluid_restart, open_boundary_restart, - boundary_restart)) + restart_with=(restart_file_fluid, + restart_file_open_boundary, + restart_file_boundary)) saving_callback = SolutionSavingCallback(dt=0.02, prefix="restart") diff --git a/src/TrixiParticles.jl b/src/TrixiParticles.jl index 0a5d0d5447..4740e88b65 100644 --- a/src/TrixiParticles.jl +++ b/src/TrixiParticles.jl @@ -62,7 +62,7 @@ include("io/io.jl") include("general/restart.jl") include("visualization/recipes_plots.jl") -export Semidiscretization, semidiscretize, restart_with!, RestartCondition +export Semidiscretization, semidiscretize, restart_with! export InitialCondition export WeaklyCompressibleSPHSystem, EntropicallyDampedSPHSystem, TotalLagrangianSPHSystem, WallBoundarySystem, DEMSystem, BoundaryDEMSystem, OpenBoundarySystem, diff --git a/src/general/restart.jl b/src/general/restart.jl index 75de2d8822..d8488a24f1 100644 --- a/src/general/restart.jl +++ b/src/general/restart.jl @@ -1,88 +1,41 @@ -""" - RestartCondition(system::AbstractSystem, filename; output_directory="out") - -Create a `RestartCondition` for the given `system` from the specified `filename`. - -# Arguments -- `system`: The system to restart. -- `filename`: The name of the file from which to restart. This file should be in - VTK format and correspond to the specified `system`. - -# Keywords -- `output_directory`: The directory where the restart file is located (default: `"out"`). -""" -struct RestartCondition{V, U} - system :: AbstractSystem - v_restart :: V - u_restart :: U - t_restart :: Real -end - -function RestartCondition(system::AbstractSystem, filename; output_directory="out") - if !occursin(vtkname(system), basename(splitext(filename)[1])) - throw(ArgumentError("Filename '$filename' does not seem to correspond to system of type $(nameof(typeof(system))).")) - end - - restart_file = joinpath(output_directory, filename) - restart_data = vtk2trixi(restart_file) - v_restart = restart_v(system, restart_data) - u_restart = restart_u(system, restart_data) - - precondition_system!(system, restart_file) - - t_restart = convert(eltype(system), restart_data.time) - - return RestartCondition(system, v_restart, u_restart, t_restart) -end - -function Base.show(io::IO, rc::RestartCondition) - @nospecialize rc # reduce precompilation time - - print(io, "RestartCondition{$(nameof(typeof(rc.system)))}()") -end - -function Base.show(io::IO, ::MIME"text/plain", rc::RestartCondition) - @nospecialize rc # reduce precompilation time - - if get(io, :compact, false) - show(io, rc) - else - summary_header(io, "RestartCondition") - summary_line(io, "System", "$(nameof(typeof(rc.system)))") - summary_line(io, "#particles u", "$(size(rc.u_restart, 2))") - summary_line(io, "#particles v", "$(size(rc.v_restart, 2))") - summary_line(io, "eltype u", "$(eltype(rc.u_restart))") - summary_line(io, "eltype v", "$(eltype(rc.v_restart))") - summary_footer(io) - end -end - -function set_initial_conditions!(v0_ode, u0_ode, semi, restart_conditions) +function set_initial_conditions!(v0_ode, u0_ode, semi, restart_with::Tuple{Vararg{String}}) # Check number of systems - if length(semi.systems) != length(restart_conditions) - throw(ArgumentError("Number of systems in `semi` does not match number of `restart_conditions`")) + if length(semi.systems) != length(restart_with) + throw(ArgumentError("Number of systems in `semi` does not match number of restart files provided " * + "in `restart_with`")) end # Check that systems match + expected_system_names = system_names(semi.systems) foreach_system(semi) do system system_index = system_indices(system, semi) - if !(system == restart_conditions[system_index].system) - throw(ArgumentError("System at index $system_index in `semi` does not match system in `restart_conditions`")) + filename = restart_with[system_index] + expected_system_name = expected_system_names[system_index] + if !occursin(expected_system_name, basename(splitext(filename)[1])) + throw(ArgumentError("Filename '$filename' for system $system_index does not contain expected name '$expected_system_name'. " * + "Expected a VTK file for system of type $(nameof(typeof(system))).")) end end # Set initial conditions - foreach_noalloc(semi.systems, restart_conditions) do (system, restart_condition) + foreach_noalloc(semi.systems, restart_with) do (system, restart_file) v0_system = wrap_v(v0_ode, system, semi) u0_system = wrap_u(u0_ode, system, semi) - v0_system .= Adapt.adapt(semi.parallelization_backend, restart_condition.v_restart) - u0_system .= Adapt.adapt(semi.parallelization_backend, restart_condition.u_restart) + restart_data = vtk2trixi(restart_file) + v_restart = restart_v(system, restart_data) + u_restart = restart_u(system, restart_data) + + v0_system .= Adapt.adapt(semi.parallelization_backend, v_restart) + u0_system .= Adapt.adapt(semi.parallelization_backend, u_restart) + + precondition_system!(system, restart_file) end end -function time_span(tspan, restart_conditions) - t_restart = first(restart_conditions).t_restart +function time_span(tspan, restart_with::Tuple{Vararg{String}}) + restart_data = vtk2trixi(first(restart_with)) + t_restart = convert(eltype(tspan), restart_data.time) if !isapprox(tspan[1], t_restart) @info "Adjusting initial time from $(tspan[1]) to restart time $t_restart" @@ -115,7 +68,8 @@ end precondition_system!(system, restart_file) = system -function initialize_neighborhood_searches!(semi, u0_ode, restart_conditions) +function initialize_neighborhood_searches!(semi, u0_ode, + restart_with::Tuple{Vararg{String}}) foreach_system(semi) do system foreach_system(semi) do neighbor # TODO Initialize after adapting to the GPU. @@ -142,7 +96,7 @@ function initial_restart_coordinates(system::Union{WallBoundarySystem, return initial_coordinates(system) end -function initialize!(semi::Semidiscretization, restart_conditions) +function initialize!(semi::Semidiscretization, restart_with::Tuple{Vararg{String}}) foreach_system(semi) do system # Initialize this system initialize_restart!(system, semi) diff --git a/src/general/semidiscretization.jl b/src/general/semidiscretization.jl index 35bcf55a29..0e68818e26 100644 --- a/src/general/semidiscretization.jl +++ b/src/general/semidiscretization.jl @@ -253,7 +253,7 @@ end @inline foreach_system(f, systems) = foreach_noalloc(f, systems) """ - semidiscretize(semi, tspan; reset_threads=true, restart_conditions=nothing) + semidiscretize(semi, tspan; reset_threads=true, restart_with=nothing) Create an `ODEProblem` from the semidiscretization with the specified `tspan`. @@ -262,9 +262,10 @@ Create an `ODEProblem` from the semidiscretization with the specified `tspan`. - `tspan`: The time span over which the simulation will be run. # Keywords -- `restart_conditions`: A tuple of [`RestartCondition`](@ref) objects specifying - from which files to restart each system. The order has to match the order of systems - in `semi`. By default, no restart is performed. +- `restart_with`: Can be used to restart the simulation from VTK solution files (see [`SolutionSavingCallback`](@ref)). + This has to be a tuple of filenames, one for each system in the [`Semidiscretization`](@ref). + The order of the filenames has to match the order of the systems in the [`Semidiscretization`](@ref). + If no restart is desired, use `nothing` (default). - `reset_threads`: A boolean flag to reset Polyester.jl threads before the simulation (default: `true`). After an error within a threaded loop, threading might be disabled. Resetting the threads before the simulation ensures that threading is enabled again for the simulation. @@ -291,7 +292,7 @@ timespan: (0.0, 1.0) u0: ([...], [...]) *this line is ignored by filter* ``` """ -function semidiscretize(semi, tspan; reset_threads=true, restart_conditions=nothing) +function semidiscretize(semi, tspan; reset_threads=true, restart_with=nothing) (; systems) = semi # Check that all systems have the same eltype @@ -332,11 +333,11 @@ function semidiscretize(semi, tspan; reset_threads=true, restart_conditions=noth end # Set initial condition - set_initial_conditions!(v0_ode, u0_ode, semi, restart_conditions) + set_initial_conditions!(v0_ode, u0_ode, semi, restart_with) # TODO initialize after adapting to the GPU. # Requires https://github.com/trixi-framework/PointNeighbors.jl/pull/86. - initialize_neighborhood_searches!(semi, u0_ode, restart_conditions) + initialize_neighborhood_searches!(semi, u0_ode, restart_with) if semi.parallelization_backend isa KernelAbstractions.Backend # Convert all arrays to the correct array type. @@ -360,16 +361,16 @@ function semidiscretize(semi, tspan; reset_threads=true, restart_conditions=noth end # Initialize all particle systems - initialize!(semi_new, restart_conditions) + initialize!(semi_new, restart_with) # Reset callback flag that will be set by the `UpdateCallback` semi_new.update_callback_used[] = false return DynamicalODEProblem(kick!, drift!, v0_ode, u0_ode, - time_span(tspan, restart_conditions), semi_new) + time_span(tspan, restart_with), semi_new) end -function set_initial_conditions!(v0_ode, u0_ode, semi, restart_conditions::Nothing) +function set_initial_conditions!(v0_ode, u0_ode, semi, restart_with::Nothing) foreach_system(semi) do system v0_system = wrap_v(v0_ode, system, semi) u0_system = wrap_u(u0_ode, system, semi) @@ -379,9 +380,9 @@ function set_initial_conditions!(v0_ode, u0_ode, semi, restart_conditions::Nothi end end -time_span(tspan, restart_conditions::Nothing) = tspan +time_span(tspan, restart_with::Nothing) = tspan -function initialize!(semi::Semidiscretization, restart_conditions::Nothing) +function initialize!(semi::Semidiscretization, restart_with::Nothing) foreach_system(semi) do system # Initialize this system initialize!(system, semi) @@ -425,7 +426,7 @@ function restart_with!(semi, sol; reset_threads=true) return semi end -function initialize_neighborhood_searches!(semi, u0_ode, restart_conditions::Nothing) +function initialize_neighborhood_searches!(semi, u0_ode, restart_with::Nothing) initialize_neighborhood_searches!(semi) end diff --git a/test/examples/gpu.jl b/test/examples/gpu.jl index 4672bae45c..8dcfb422f7 100644 --- a/test/examples/gpu.jl +++ b/test/examples/gpu.jl @@ -678,15 +678,13 @@ end semi_new = TrixiParticles.Adapt.adapt(Array, sol.prob.p) iter = round(Int, 0.3 / 0.02) - fluid_restart = RestartCondition(semi_new.systems[1], "fluid_1_$iter.vtu") - open_boundary_restart = RestartCondition(semi_new.systems[2], - "open_boundary_1_$iter.vtu") - boundary_restart = RestartCondition(semi_new.systems[3], "boundary_1_$iter.vtu") + fluid_restart = joinpath("out", "fluid_1_$iter.vtu") + open_boundary_restart = joinpath("out", "open_boundary_1_$iter.vtu") + boundary_restart = joinpath("out", "boundary_1_$iter.vtu") ode_restart = semidiscretize(semi_new, (0.3f0, 0.6f0); - restart_conditions=(fluid_restart, - open_boundary_restart, - boundary_restart)) + restart_with=(fluid_restart, open_boundary_restart, + boundary_restart)) sol_restart = solve(ode_restart, RDPK3SpFSAL35(), abstol=1.0f-5, reltol=1.0f-3, dtmax=1.0f-2, save_everystep=false, diff --git a/test/general/restart.jl b/test/general/restart.jl index c2ddc609c9..1af3496707 100644 --- a/test/general/restart.jl +++ b/test/general/restart.jl @@ -19,13 +19,13 @@ tspan=(0.0, 0.3), sound_speed_factor=10, particle_spacing=4e-5) iter = round(Int, 0.3 / 0.02) - fluid_restart = RestartCondition(fluid_system, "fluid_1_$iter.vtu") - open_boundary_restart = RestartCondition(open_boundary, "open_boundary_1_$iter.vtu") - boundary_restart = RestartCondition(boundary_system, "boundary_1_$iter.vtu") + fluid_restart = joinpath("out", "fluid_1_$iter.vtu") + open_boundary_restart = joinpath("out", "open_boundary_1_$iter.vtu") + boundary_restart = joinpath("out", "boundary_1_$iter.vtu") ode_restart = semidiscretize(semi, (0.3, 0.6); - restart_conditions=(fluid_restart, open_boundary_restart, - boundary_restart)) + restart_with=(fluid_restart, open_boundary_restart, + boundary_restart)) sol_restart = solve(ode_restart, RDPK3SpFSAL35(), abstol=1e-5, reltol=1e-3, dtmax=1e-2, save_everystep=false, callback=UpdateCallback()) From 7564985ed55bed65d8aa5c59cfd6805c470d3ed6 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Wed, 7 Jan 2026 16:30:43 +0100 Subject: [PATCH 048/134] add test mixed types --- test/io/read_vtk.jl | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/io/read_vtk.jl b/test/io/read_vtk.jl index cdb350931b..ceef5aede4 100644 --- a/test/io/read_vtk.jl +++ b/test/io/read_vtk.jl @@ -54,6 +54,20 @@ @test eltype(test_ic) === Float32 @test eltype(test_ic.coordinates) === Float32 end + + @testset verbose=true "Custom Element Type Mixed" begin + trixi2vtk(expected_ic; filename="tmp_initial_condition_64", + output_directory=tmp_dir) + file = joinpath(tmp_dir, "tmp_initial_condition_64.vtu") + test_ic = vtk2trixi(file, element_type=Float32, coordinates_eltype=Float64) + + @test isapprox(expected_ic.coordinates, test_ic.coordinates, rtol=1e-5) + @test isapprox(expected_ic.velocity, test_ic.velocity, rtol=1e-5) + @test isapprox(expected_ic.density, test_ic.density, rtol=1e-5) + @test isapprox(expected_ic.pressure, test_ic.pressure, rtol=1e-5) + @test eltype(test_ic) === Float32 + @test eltype(test_ic.coordinates) === Float64 + end end @testset verbose=true "`AbstractFluidSystem`" begin From cfba71cec03787d0e36e9d64c4703917fcd8ea50 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Sat, 10 Jan 2026 14:52:17 +0100 Subject: [PATCH 049/134] add NEWS entry --- NEWS.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NEWS.md b/NEWS.md index a026fa85b3..dac772ece5 100644 --- a/NEWS.md +++ b/NEWS.md @@ -10,6 +10,8 @@ used in the Julia ecosystem. Notable changes will be documented in this file for - Keyword argument `n_clamped_particles` of the `TotalLagrangianSPHSystem` has been deprecated in favor of a new kwarg `clamped_particles`. +- Return type of `vtk2trixi` changed to `NamedTuple` including an optional + `:initial_condition` field if `create_initial_condition=true` is passed. (#959) ### Features From 2dcbc76c4cbd480aaa66d5f51149ee8697226f6f Mon Sep 17 00:00:00 2001 From: LasNikas Date: Sat, 10 Jan 2026 14:52:36 +0100 Subject: [PATCH 050/134] revise --- src/io/read_vtk.jl | 60 +++++++++++------ test/io/read_vtk.jl | 159 +++++++++++++++++++++++++++++--------------- 2 files changed, 147 insertions(+), 72 deletions(-) diff --git a/src/io/read_vtk.jl b/src/io/read_vtk.jl index de095aaf00..2fb3ec4324 100644 --- a/src/io/read_vtk.jl +++ b/src/io/read_vtk.jl @@ -1,15 +1,14 @@ """ - vtk2trixi(file::String; custom_quantities...) + vtk2trixi(file::String; element_type=nothing, coordinates_eltype=nothing, + create_initial_condition=true, custom_quantities...) -Load VTK file and convert data to a NamedTuple. +Read a VTK file and return a `NamedTuple` with keys +`:coordinates`, `:velocity`, `:density`, `:pressure`, `:particle_spacing`, `:time`, +plus any requested custom quantities. +Missing fields are zero-filled; `:particle_spacing` is scalar if constant, otherwise per-particle. # Arguments -- `file`: Name of the VTK file to be loaded. -- `custom_quantities...`: Additional custom quantities to be loaded from the VTK file. - Each custom quantity must be explicitly listed in the - `custom_quantities` during the simulation to ensure it is - included in the VTK output and can be successfully loaded. - See [Custom Quantities](@ref custom_quantities) for details. +- `file`: Name of the VTK file to be loaded. # Keywords - `element_type`: Element type for particle fields. By default, the type @@ -18,12 +17,20 @@ Load VTK file and convert data to a NamedTuple. - `coordinates_eltype`: Element type for particle coordinates. By default, the type stored in the VTK file is used. Otherwise, data is converted to the specified type. +- `create_initial_condition`: If `true`, an `InitialCondition` object is created + and included in the returned `NamedTuple` under + the key `:initial_condition`. Default is `true`. +- `custom_quantities...`: Keyword arguments to load additional quantities from the VTK file. + Each keyword becomes a key in the returned `NamedTuple`, with its + string value specifying the VTK field name to read. + Example: `my_data="field_name"` loads VTK field `"field_name"` + as `:my_data` in the result. !!! warning "Experimental Implementation" This is an experimental feature and may change in any future releases. # Example -```jldoctest; output = false, filter = r"density = \\[.*\\]|pressure = \\[.*\\]|mass = \\[.*\\]|velocity = \\[.*\\]|coordinates = \\[.*\\]" +```jldoctest; output = false, filter = r"density = \\[.*\\]|pressure = \\[.*\\]|velocity = \\[.*\\]|coordinates = \\[.*\\]|initial_condition = \\[.*\\]" # Create a rectangular shape rectangular = RectangularShape(0.1, (10, 10), (0, 0), density=1.5, velocity=(1.0, -2.0), pressure=1000.0) @@ -37,10 +44,11 @@ data = vtk2trixi(joinpath("out", "rectangular.vtu"); my_custom_quantity="my_custom_quantity") # output -(particle_spacing = 0.1, density = [...], time = 0.0, pressure = [...], mass = [...], my_custom_quantity = 3.0, velocity = [...], coordinates = [...]) +(particle_spacing = 0.1, density = [...], time = 0.0, pressure = [...], my_custom_quantity = 3.0, velocity = [...], coordinates = [...], initial_condition = [...]) ``` """ -function vtk2trixi(file; custom_quantities...) +function vtk2trixi(file; element_type=nothing, coordinates_eltype=nothing, + create_initial_condition=true, custom_quantities...) vtk_file = ReadVTK.VTKFile(file) # Retrieve data fields (e.g., pressure, velocity, ...) @@ -58,7 +66,7 @@ function vtk2trixi(file; custom_quantities...) ndims = first(ReadVTK.get_data(field_data["ndims"])) coordinates = convert.(cELTYPE, point_coords[1:ndims, :]) - fields = [:velocity, :density, :pressure, :mass, :particle_spacing] + fields = [:velocity, :density, :pressure, :particle_spacing] for field in fields # Look for any key that contains the field name all_keys = keys(point_data) @@ -67,8 +75,8 @@ function vtk2trixi(file; custom_quantities...) results[field] = convert.(ELTYPE, ReadVTK.get_data(point_data[all_keys[idx]])) else # Use zeros as default values when a field is missing - results[field] = string(field) in ["mass"] ? - zeros(size(coordinates, 2)) : zero(coordinates) + results[field] = string(field) in ["velocity"] ? + zero(coordinates) : zeros(ELTYPE, size(coordinates, 2)) @info "No '$field' field found in VTK file. Will be set to zero." end end @@ -78,16 +86,30 @@ function vtk2trixi(file; custom_quantities...) results[:particle_spacing] results[:coordinates] = coordinates results[:time] = "time" in keys(field_data) ? - first(ReadVTK.get_data(field_data["time"])) : 0.0 + first(ReadVTK.get_data(field_data["time"])) : zero(ELTYPE) - for (key, quantity) in custom_quantities + for (key, quantity_) in custom_quantities + quantity = string(quantity_) if quantity in keys(point_data) results[key] = ReadVTK.get_data(point_data[quantity]) - end - if quantity in keys(field_data) + elseif quantity in keys(field_data) results[key] = first(ReadVTK.get_data(field_data[quantity])) + else + throw(ArgumentError("Custom quantity '$quantity' not found in VTK file. " * + "Make sure it was included during the simulation.")) end end - return NamedTuple(results) + results = NamedTuple(results) + + if create_initial_condition + ic = InitialCondition(; coordinates=results.coordinates, + particle_spacing=results.particle_spacing, + velocity=results.velocity, density=results.density, + pressure=results.pressure) + + return merge(results, (initial_condition=ic,)) + else + return results + end end diff --git a/test/io/read_vtk.jl b/test/io/read_vtk.jl index c603c597e0..9a3d720553 100644 --- a/test/io/read_vtk.jl +++ b/test/io/read_vtk.jl @@ -3,59 +3,118 @@ coordinates = fill(1.0, 2, 12) velocity = fill(2.0, 2, 12) - expected_ic = InitialCondition(coordinates=coordinates, velocity=velocity, - density=1000.0, pressure=900.0, mass=50.0) + expected_data = InitialCondition(coordinates=coordinates, velocity=velocity, + density=1000.0, pressure=900.0, + particle_spacing=0.1) expected_cq_scalar = 3.0 - expected_cq_vector = fill(expected_cq_scalar, - size(expected_ic.coordinates, 2)) + expected_cq_vector = fill(expected_cq_scalar, nparticles(expected_data)) scalar_cq_function(system, data, t) = expected_cq_scalar - vector_cq_function(system, data, - t) = fill(expected_cq_scalar, nparticles(system)) + vector_cq_function(system, data, t) = fill(expected_cq_scalar, nparticles(system)) @testset verbose=true "`InitialCondition`" begin - @testset verbose=true "Scalar custom quantity" begin - trixi2vtk(expected_ic; filename="tmp_initial_condition_scalar", - output_directory=tmp_dir, - cq_scalar=expected_cq_scalar) + @testset verbose=true "`Float64`" begin + trixi2vtk(expected_data; filename="tmp_initial_condition_64", + output_directory=tmp_dir) + file = joinpath(tmp_dir, "tmp_initial_condition_64.vtu") + data = vtk2trixi(file) + + @test isapprox(expected_data.coordinates, data.coordinates, rtol=1e-5) + @test isapprox(expected_data.velocity, data.velocity, rtol=1e-5) + @test isapprox(expected_data.density, data.density, rtol=1e-5) + @test isapprox(expected_data.pressure, data.pressure, rtol=1e-5) + @test all(key -> eltype(data[key]) === Float64, keys(data)) + @test eltype(data.coordinates) === Float64 + end + + @testset verbose=true "`Float32`" begin + expected_ic_32 = InitialCondition(; + coordinates=convert.(Float32, + coordinates), + velocity=convert.(Float32, velocity), + density=1000.0f0, pressure=900.0f0, + mass=50.0f0, particle_spacing=0.1f0) + trixi2vtk(expected_ic_32; filename="tmp_initial_condition_32", + output_directory=tmp_dir) + file = joinpath(tmp_dir, "tmp_initial_condition_32.vtu") + data = vtk2trixi(file) + + @test isapprox(expected_ic_32.coordinates, data.coordinates, rtol=1e-5) + @test isapprox(expected_ic_32.velocity, data.velocity, rtol=1e-5) + @test isapprox(expected_ic_32.density, data.density, rtol=1e-5) + @test isapprox(expected_ic_32.pressure, data.pressure, rtol=1e-5) + @test all(key -> eltype(data[key]) === Float32, keys(data)) + @test eltype(data.coordinates) === Float32 + end + + @testset verbose=true "Custom Element Type" begin + trixi2vtk(expected_data; filename="tmp_initial_condition_64", + output_directory=tmp_dir) + file = joinpath(tmp_dir, "tmp_initial_condition_64.vtu") + data = vtk2trixi(file, element_type=Float32, coordinates_eltype=Float32) + + @test isapprox(expected_data.coordinates, data.coordinates, rtol=1e-5) + @test isapprox(expected_data.velocity, data.velocity, rtol=1e-5) + @test isapprox(expected_data.density, data.density, rtol=1e-5) + @test isapprox(expected_data.pressure, data.pressure, rtol=1e-5) + @test all(key -> eltype(data[key]) === Float32, keys(data)) + @test eltype(data.coordinates) === Float32 + end + + @testset verbose=true "Scalar Custom Quantity" begin + trixi2vtk(expected_data; filename="tmp_initial_condition_scalar", + output_directory=tmp_dir, cq_scalar=expected_cq_scalar) # Load file containing test data test_data = vtk2trixi(joinpath(tmp_dir, "tmp_initial_condition_scalar.vtu"); - cq_scalar="cq_scalar") + cq_scalar=:cq_scalar) - @test isapprox(expected_ic.coordinates, test_data.coordinates, rtol=1e-5) - @test isapprox(expected_ic.velocity, test_data.velocity, rtol=1e-5) - @test isapprox(expected_ic.density, test_data.density, rtol=1e-5) - @test isapprox(expected_ic.pressure, test_data.pressure, rtol=1e-5) - @test isapprox(expected_cq_scalar, test_data.cq_scalar, - rtol=1e-5) + @test isapprox(expected_data.coordinates, test_data.coordinates, rtol=1e-5) + @test isapprox(expected_data.velocity, test_data.velocity, rtol=1e-5) + @test isapprox(expected_data.density, test_data.density, rtol=1e-5) + @test isapprox(expected_data.pressure, test_data.pressure, rtol=1e-5) + @test isapprox(expected_cq_scalar, test_data.cq_scalar, rtol=1e-5) end - @testset verbose=true "Vector custom quantity" begin - trixi2vtk(expected_ic; filename="tmp_initial_condition_vector", + @testset verbose=true "Vector Custom Quantity" begin + trixi2vtk(expected_data; filename="tmp_initial_condition_vector", output_directory=tmp_dir, cq_vector=expected_cq_vector) # Load file containing test data test_data = vtk2trixi(joinpath(tmp_dir, "tmp_initial_condition_vector.vtu"); - cq_vector="cq_vector") + cq_vector=:cq_vector) - @test isapprox(expected_ic.coordinates, test_data.coordinates, rtol=1e-5) - @test isapprox(expected_ic.velocity, test_data.velocity, rtol=1e-5) - @test isapprox(expected_ic.density, test_data.density, rtol=1e-5) - @test isapprox(expected_ic.pressure, test_data.pressure, rtol=1e-5) - @test isapprox(expected_cq_vector, test_data.cq_vector, - rtol=1e-5) + @test isapprox(expected_data.coordinates, test_data.coordinates, rtol=1e-5) + @test isapprox(expected_data.velocity, test_data.velocity, rtol=1e-5) + @test isapprox(expected_data.density, test_data.density, rtol=1e-5) + @test isapprox(expected_data.pressure, test_data.pressure, rtol=1e-5) + @test isapprox(expected_cq_vector, test_data.cq_vector, rtol=1e-5) + end + + @testset verbose=true "Custom Element Type Mixed" begin + trixi2vtk(expected_data; filename="tmp_initial_condition_64", + output_directory=tmp_dir) + file = joinpath(tmp_dir, "tmp_initial_condition_64.vtu") + data = vtk2trixi(file, element_type=Float32, coordinates_eltype=Float64) + + @test isapprox(expected_data.coordinates, data.coordinates, rtol=1e-5) + @test isapprox(expected_data.velocity, data.velocity, rtol=1e-5) + @test isapprox(expected_data.density, data.density, rtol=1e-5) + @test isapprox(expected_data.pressure, data.pressure, rtol=1e-5) + @test all(key -> eltype(data[key]) === Float32, + setdiff(keys(data), (:coordinates,))) + @test eltype(data.coordinates) === Float64 end end @testset verbose=true "`AbstractFluidSystem`" begin - fluid_system = EntropicallyDampedSPHSystem(expected_ic, + fluid_system = EntropicallyDampedSPHSystem(expected_data, SchoenbergCubicSplineKernel{2}(), 1.5, 1.5) # Overwrite values because we skip the update step - fluid_system.cache.density .= expected_ic.density + fluid_system.cache.density .= expected_data.density semi = Semidiscretization(fluid_system) @@ -71,33 +130,30 @@ vu_ode = (; x) dvdu_ode = (; x=(; v_ode=dv_ode, u_ode=du_ode)) - @testset verbose=true "Scalar custom quantity" begin + @testset verbose=true "Scalar Custom Quantity" begin trixi2vtk(fluid_system, dvdu_ode, vu_ode, semi, 0.0, nothing; system_name="tmp_file_fluid_scalar", - output_directory=tmp_dir, - iter=1, cq_scalar=scalar_cq_function) + output_directory=tmp_dir, iter=1, cq_scalar=scalar_cq_function) # Load file containing test data test_data = vtk2trixi(joinpath(tmp_dir, "tmp_file_fluid_scalar_1.vtu"); - cq_scalar="cq_scalar") + cq_scalar=:cq_scalar) @test isapprox(u, test_data.coordinates, rtol=1e-5) @test isapprox(v, test_data.velocity, rtol=1e-5) @test isapprox(pressure, test_data.pressure, rtol=1e-5) @test isapprox(fluid_system.cache.density, test_data.density, rtol=1e-5) - @test isapprox(expected_cq_scalar, test_data.cq_scalar, - rtol=1e-5) + @test isapprox(expected_cq_scalar, test_data.cq_scalar, rtol=1e-5) end - @testset verbose=true "Vector custom quantity" begin + @testset verbose=true "Vector Custom Quantity" begin trixi2vtk(fluid_system, dvdu_ode, vu_ode, semi, 0.0, nothing; system_name="tmp_file_fluid_vector", - output_directory=tmp_dir, - iter=1, cq_vector=vector_cq_function) + output_directory=tmp_dir, iter=1, cq_vector=vector_cq_function) # Load file containing test data test_data = vtk2trixi(joinpath(tmp_dir, "tmp_file_fluid_vector_1.vtu"); - cq_vector="cq_vector") + cq_vector=:cq_vector) @test isapprox(u, test_data.coordinates, rtol=1e-5) @test isapprox(v, test_data.velocity, rtol=1e-5) @@ -109,17 +165,17 @@ end @testset verbose=true "`WallBoundarySystem`" begin - boundary_model = BoundaryModelDummyParticles(expected_ic.density, - expected_ic.mass, + boundary_model = BoundaryModelDummyParticles(expected_data.density, + expected_data.mass, SummationDensity(), SchoenbergCubicSplineKernel{2}(), 1.5) # Overwrite values because we skip the update step - boundary_model.pressure .= expected_ic.pressure - boundary_model.cache.density .= expected_ic.density + boundary_model.pressure .= expected_data.pressure + boundary_model.cache.density .= expected_data.density - boundary_system = WallBoundarySystem(expected_ic, boundary_model) + boundary_system = WallBoundarySystem(expected_data, boundary_model) semi = Semidiscretization(boundary_system) # Create dummy ODE solutions @@ -131,15 +187,14 @@ vu_ode = (; x) dvdu_ode = (; x=(; v_ode=dv_ode, u_ode=du_ode)) - @testset verbose=true "Scalar custom quantity" begin + @testset verbose=true "Scalar Custom Quantity" begin trixi2vtk(boundary_system, dvdu_ode, vu_ode, semi, 0.0, nothing; system_name="tmp_file_boundary_scalar", - output_directory=tmp_dir, - iter=1, cq_scalar=scalar_cq_function) + output_directory=tmp_dir, iter=1, cq_scalar=scalar_cq_function) # Load file containing test data test_data = vtk2trixi(joinpath(tmp_dir, "tmp_file_boundary_scalar_1.vtu"); - cq_scalar="cq_scalar") + cq_scalar=:cq_scalar) @test isapprox(boundary_system.coordinates, test_data.coordinates, rtol=1e-5) @@ -148,19 +203,17 @@ rtol=1e-5) @test isapprox(boundary_model.pressure, test_data.pressure, rtol=1e-5) @test isapprox(boundary_model.cache.density, test_data.density, rtol=1e-5) - @test isapprox(expected_cq_scalar, test_data.cq_scalar, - rtol=1e-5) + @test isapprox(expected_cq_scalar, test_data.cq_scalar, rtol=1e-5) end - @testset verbose=true "Vector custom quantity" begin + @testset verbose=true "Vector Custom Quantity" begin trixi2vtk(boundary_system, dvdu_ode, vu_ode, semi, 0.0, nothing; system_name="tmp_file_boundary_vector", - output_directory=tmp_dir, - iter=1, cq_vector=vector_cq_function) + output_directory=tmp_dir, iter=1, cq_vector=vector_cq_function) # Load file containing test data test_data = vtk2trixi(joinpath(tmp_dir, "tmp_file_boundary_vector_1.vtu"); - cq_vector="cq_vector") + cq_vector=:cq_vector) @test isapprox(boundary_system.coordinates, test_data.coordinates, rtol=1e-5) From 0d587cd5b439e068f148a2589dc670a21b8b48d1 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Sat, 10 Jan 2026 15:38:02 +0100 Subject: [PATCH 051/134] fix doc test --- src/io/read_vtk.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/io/read_vtk.jl b/src/io/read_vtk.jl index 2fb3ec4324..97d524f8d4 100644 --- a/src/io/read_vtk.jl +++ b/src/io/read_vtk.jl @@ -30,7 +30,7 @@ Missing fields are zero-filled; `:particle_spacing` is scalar if constant, other This is an experimental feature and may change in any future releases. # Example -```jldoctest; output = false, filter = r"density = \\[.*\\]|pressure = \\[.*\\]|velocity = \\[.*\\]|coordinates = \\[.*\\]|initial_condition = \\[.*\\]" +```jldoctest; output = false, filter = r"density = \\[.*\\]|pressure = \\[.*\\]|velocity = \\[.*\\]|coordinates = \\[.*\\]" # Create a rectangular shape rectangular = RectangularShape(0.1, (10, 10), (0, 0), density=1.5, velocity=(1.0, -2.0), pressure=1000.0) @@ -44,7 +44,7 @@ data = vtk2trixi(joinpath("out", "rectangular.vtu"); my_custom_quantity="my_custom_quantity") # output -(particle_spacing = 0.1, density = [...], time = 0.0, pressure = [...], my_custom_quantity = 3.0, velocity = [...], coordinates = [...], initial_condition = [...]) +(particle_spacing = 0.1, density = [...], time = 0.0, pressure = [...], my_custom_quantity = 3.0, velocity = [...], coordinates = [...], initial_condition = InitialCondition{Float64, Float64}()) ``` """ function vtk2trixi(file; element_type=nothing, coordinates_eltype=nothing, From 48a79e1c1c78d3dd1ca28935c4ff5430474bcb77 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Sat, 10 Jan 2026 15:54:16 +0100 Subject: [PATCH 052/134] rm test file --- .../boundary/open_boundary/flow_rate.jl | 64 ------------------- 1 file changed, 64 deletions(-) delete mode 100644 test/schemes/boundary/open_boundary/flow_rate.jl diff --git a/test/schemes/boundary/open_boundary/flow_rate.jl b/test/schemes/boundary/open_boundary/flow_rate.jl deleted file mode 100644 index 9bbaf0e277..0000000000 --- a/test/schemes/boundary/open_boundary/flow_rate.jl +++ /dev/null @@ -1,64 +0,0 @@ -@testset verbose=true "Calculate Flow Rate" begin - particle_spacing = 0.01 - - # Define a parabolic velocity profile - velocity_function(pos) = [4 * (pos[2] - 0.5) * (1.5 - pos[2]), 0.0] - - # Create fluid domain with the specified velocity profile - n_particles_y = round(Int, 2 / particle_spacing) - fluid = RectangularShape(particle_spacing, (4, n_particles_y), (0.0, 0.0), - density=1000.0, velocity=velocity_function) - - smoothing_length = 1.3 * particle_spacing - smoothing_kernel = WendlandC2Kernel{ndims(fluid)}() - fluid_system = EntropicallyDampedSPHSystem(fluid, smoothing_kernel, smoothing_length, - 1.0) - fluid_system.cache.density .= fluid.density - - # Use a smaller cross-sectional area to test user-defined area functionality - # and to perform interpolation within an embedded domain. - # (In a simulation with solid boundaries, wall velocities would also be included.) - sample_points = RectangularShape(particle_spacing, (1, round(Int, n_particles_y / 2)), - (0.0, 0.5), density=1.0).coordinates - - zone = BoundaryZone(; boundary_face=([0.0, 0.0], [0.0, 2.0]), - face_normal=(-1.0, 0.0), open_boundary_layers=10, density=1000.0, - particle_spacing, sample_points=sample_points, - reference_velocity=(pos, t) -> velocity_function(pos)) - - open_boundary = OpenBoundarySystem(zone; fluid_system, - boundary_model=BoundaryModelMirroringTafuni(), - calculate_flow_rate=true, buffer_size=0) - - semi = Semidiscretization(fluid_system, open_boundary) - TrixiParticles.initialize_neighborhood_searches!(semi) - TrixiParticles.initialize!(open_boundary, semi) - - # Set up ODE for initial conditions - ode = semidiscretize(semi, (0, 1)) - v_ode, u_ode = ode.u0.x - - boundary_zone = first(open_boundary.boundary_zones) - - @testset verbose=true "Velocity Interpolation" begin - TrixiParticles.interpolate_velocity!(open_boundary, boundary_zone, - v_ode, u_ode, semi) - - coords = reinterpret(reshape, SVector{2, Float64}, - boundary_zone.cache.sample_points) - vels_analytic = first.(velocity_function.(coords)) - vels_interpolated = first.(reinterpret(reshape, SVector{2, Float64}, - boundary_zone.cache.sample_velocity)) - - @test isapprox(vels_interpolated, vels_analytic, rtol=1e-3) - end - - @testset verbose=true "Flow Rate Calculation" begin - TrixiParticles.calculate_flow_rate!(open_boundary, v_ode, u_ode, semi) - - Q_analytic = zone.cache.cross_sectional_area * (2 / 3) - Q_calculated = first(open_boundary.cache.boundary_zones_flow_rate)[] - - @test isapprox(Q_analytic, Q_calculated; rtol=1e-3) - end -end From f264c9a6d9d04ea5d56b1af3cbefe0b7cda2df03 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Sat, 10 Jan 2026 16:00:23 +0100 Subject: [PATCH 053/134] fix merge bugs --- src/io/write_vtk.jl | 10 --- src/schemes/boundary/open_boundary/system.jl | 64 ++++++++++++++++++++ 2 files changed, 64 insertions(+), 10 deletions(-) diff --git a/src/io/write_vtk.jl b/src/io/write_vtk.jl index 685ce5b095..6b5b83cd6c 100644 --- a/src/io/write_vtk.jl +++ b/src/io/write_vtk.jl @@ -411,16 +411,6 @@ function write2vtk!(vtk, v, u, t, system::OpenBoundarySystem) vtk["zone_id"] = [system.boundary_zone_indices[particle] for particle in eachparticle(system)] - if system.cache.calculate_flow_rate - Q_total = zero(eltype(system)) - for i in eachindex(system.cache.boundary_zones_flow_rate) - vtk["Q_$i"] = system.cache.boundary_zones_flow_rate[i][] - Q_total += system.cache.boundary_zones_flow_rate[i][] - end - - vtk["Q_total"] = Q_total - end - if any(pm -> isa(pm, AbstractPressureModel), system.cache.pressure_reference_values) for (i, pressure_model) in enumerate(system.cache.pressure_reference_values) if pressure_model isa AbstractPressureModel diff --git a/src/schemes/boundary/open_boundary/system.jl b/src/schemes/boundary/open_boundary/system.jl index dc3f150cf8..fdae2dd496 100644 --- a/src/schemes/boundary/open_boundary/system.jl +++ b/src/schemes/boundary/open_boundary/system.jl @@ -678,3 +678,67 @@ function interpolate_velocity!(system::OpenBoundarySystem, boundary_zone, return system end + +function restart_u(system::OpenBoundarySystem, data) + coords_total = zeros(coordinates_eltype(system), u_nvariables(system), + n_integrated_particles(system)) + coords_total .= coordinates_eltype(system)(1e16) + + coords_active = data.coordinates + for particle in axes(coords_active, 2) + for dim in 1:ndims(system) + coords_total[dim, particle] = coords_active[dim, particle] + end + end + + system.buffer.active_particle .= false + system.buffer.active_particle[1:size(coords_active, 2)] .= true + + update_system_buffer!(system.buffer) + + return coords_total +end + +function restart_v(system::OpenBoundarySystem, data) + v_total = zeros(eltype(system), v_nvariables(system), + n_integrated_particles(system)) + + v_active = zeros(eltype(system), v_nvariables(system), size(data.velocity, 2)) + + v_active[1:ndims(system), :] = data.velocity + write_density_and_pressure!(v_active, system.fluid_system, + density_calculator(system), data.pressure, data.density) + + for particle in axes(v_active, 2) + for i in axes(v_active, 1) + v_total[i, particle] = v_active[i, particle] + end + end + + return v_total +end + +function precondition_system!(system::OpenBoundarySystem, file) + # We cannot simply use `update_boundary_zone_indices!` because rounding errors during file I/O + # may result in particles being located outside their intended boundary zone, even though they + # were written as active particles. + set_zero!(system.boundary_zone_indices) + values = vtk2trixi(file; zone_id="zone_id") + system.boundary_zone_indices[each_integrated_particle(system)] .= values.zone_id + + if any(pm -> isa(pm, AbstractPressureModel), system.cache.pressure_reference_values) + keys_p = [(Symbol(:p, i), "boundary_zone_pressure_$(i)") + for i in eachindex(system.boundary_zones)] + keys_Q = [(Symbol(:Q, i), "Q_$(i)") for i in eachindex(system.boundary_zones)] + values = vtk2trixi(file; merge(keys_p, keys_Q)...) + + for (i, pressure_model) in enumerate(system.cache.pressure_reference_values) + if pressure_model isa AbstractPressureModel + pressure_model.pressure[] = values[Symbol(:p, i)] + pressure_model.flow_rate[] = values[Symbol(:Q, i)] + end + end + end + + return system +end From df63263aad3fb501689e74075f7b62d586912b2d Mon Sep 17 00:00:00 2001 From: LasNikas Date: Sat, 10 Jan 2026 17:04:29 +0100 Subject: [PATCH 054/134] fix preconditioning --- src/schemes/boundary/open_boundary/system.jl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/schemes/boundary/open_boundary/system.jl b/src/schemes/boundary/open_boundary/system.jl index fdae2dd496..53509faf89 100644 --- a/src/schemes/boundary/open_boundary/system.jl +++ b/src/schemes/boundary/open_boundary/system.jl @@ -727,10 +727,12 @@ function precondition_system!(system::OpenBoundarySystem, file) system.boundary_zone_indices[each_integrated_particle(system)] .= values.zone_id if any(pm -> isa(pm, AbstractPressureModel), system.cache.pressure_reference_values) + pm_zone_ids = findall(pm -> isa(pm, AbstractPressureModel), + system.cache.pressure_reference_values) keys_p = [(Symbol(:p, i), "boundary_zone_pressure_$(i)") - for i in eachindex(system.boundary_zones)] + for i in eachindex(system.boundary_zones)[pm_zone_ids]] keys_Q = [(Symbol(:Q, i), "Q_$(i)") for i in eachindex(system.boundary_zones)] - values = vtk2trixi(file; merge(keys_p, keys_Q)...) + values = vtk2trixi(file; vcat(keys_p, keys_Q)...) for (i, pressure_model) in enumerate(system.cache.pressure_reference_values) if pressure_model isa AbstractPressureModel From 47bc4c2fce41ecea99f39dd4abfcbb57690b63e2 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Tue, 13 Jan 2026 08:32:36 +0100 Subject: [PATCH 055/134] don't read IC --- src/general/restart.jl | 2 +- src/schemes/boundary/open_boundary/system.jl | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/general/restart.jl b/src/general/restart.jl index d8488a24f1..5ee092ad5c 100644 --- a/src/general/restart.jl +++ b/src/general/restart.jl @@ -22,7 +22,7 @@ function set_initial_conditions!(v0_ode, u0_ode, semi, restart_with::Tuple{Varar v0_system = wrap_v(v0_ode, system, semi) u0_system = wrap_u(u0_ode, system, semi) - restart_data = vtk2trixi(restart_file) + restart_data = vtk2trixi(restart_file; create_initial_condition=false) v_restart = restart_v(system, restart_data) u_restart = restart_u(system, restart_data) diff --git a/src/schemes/boundary/open_boundary/system.jl b/src/schemes/boundary/open_boundary/system.jl index 53509faf89..c6adac30c1 100644 --- a/src/schemes/boundary/open_boundary/system.jl +++ b/src/schemes/boundary/open_boundary/system.jl @@ -723,7 +723,7 @@ function precondition_system!(system::OpenBoundarySystem, file) # may result in particles being located outside their intended boundary zone, even though they # were written as active particles. set_zero!(system.boundary_zone_indices) - values = vtk2trixi(file; zone_id="zone_id") + values = vtk2trixi(file; create_initial_condition=false, zone_id="zone_id") system.boundary_zone_indices[each_integrated_particle(system)] .= values.zone_id if any(pm -> isa(pm, AbstractPressureModel), system.cache.pressure_reference_values) @@ -732,7 +732,7 @@ function precondition_system!(system::OpenBoundarySystem, file) keys_p = [(Symbol(:p, i), "boundary_zone_pressure_$(i)") for i in eachindex(system.boundary_zones)[pm_zone_ids]] keys_Q = [(Symbol(:Q, i), "Q_$(i)") for i in eachindex(system.boundary_zones)] - values = vtk2trixi(file; vcat(keys_p, keys_Q)...) + values = vtk2trixi(file; create_initial_condition=false, vcat(keys_p, keys_Q)...) for (i, pressure_model) in enumerate(system.cache.pressure_reference_values) if pressure_model isa AbstractPressureModel From ae5496a284a978c52c49d60a954868d76a94dd2c Mon Sep 17 00:00:00 2001 From: LasNikas Date: Thu, 15 Jan 2026 09:24:33 +0100 Subject: [PATCH 056/134] fix merge bug --- src/general/restart.jl | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/general/restart.jl b/src/general/restart.jl index 5ee092ad5c..e5016ba847 100644 --- a/src/general/restart.jl +++ b/src/general/restart.jl @@ -72,20 +72,31 @@ function initialize_neighborhood_searches!(semi, u0_ode, restart_with::Tuple{Vararg{String}}) foreach_system(semi) do system foreach_system(semi) do neighbor - # TODO Initialize after adapting to the GPU. - # Currently, this cannot use `semi.parallelization_backend` - # because data is still on the CPU. - PointNeighbors.initialize!(get_neighborhood_search(system, neighbor, semi), - initial_restart_coordinates(system, u0_ode, semi), - initial_restart_coordinates(neighbor, u0_ode, semi), - eachindex_y=each_active_particle(neighbor), - parallelization_backend=PolyesterBackend()) + initialize_neighborhood_search!(semi, system, neighbor, u0_ode) end end return semi end +function initialize_neighborhood_search!(semi, system, neighbor, u0_ode) + # TODO Initialize after adapting to the GPU. + # Currently, this cannot use `semi.parallelization_backend` + # because data is still on the CPU. + PointNeighbors.initialize!(get_neighborhood_search(system, neighbor, semi), + initial_restart_coordinates(system, u0_ode, semi), + initial_restart_coordinates(neighbor, u0_ode, semi), + eachindex_y=each_active_particle(neighbor), + parallelization_backend=PolyesterBackend()) + return semi +end + +function initialize_neighborhood_search!(semi, system::TotalLagrangianSPHSystem, + neighbor::TotalLagrangianSPHSystem, u0_ode) + # For TLSPH, the self-interaction NHS is already initialized in the system constructor + return semi +end + function initial_restart_coordinates(system, u0_ode, semi) # Transfer to CPU if data is on the GPU. Do nothing if already on CPU. return transfer2cpu(wrap_u(u0_ode, system, semi)) From a1fc021a3cbabd6ed9e90958bb16ea52fe7658fd Mon Sep 17 00:00:00 2001 From: LasNikas Date: Tue, 20 Jan 2026 13:26:04 +0100 Subject: [PATCH 057/134] fix NEWS entry --- NEWS.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/NEWS.md b/NEWS.md index afecdb755b..cad5c3ac24 100644 --- a/NEWS.md +++ b/NEWS.md @@ -4,6 +4,13 @@ TrixiParticles.jl follows the interpretation of [semantic versioning (semver)](https://julialang.github.io/Pkg.jl/dev/compatibility/#Version-specifier-format-1) used in the Julia ecosystem. Notable changes will be documented in this file for human readability. +## Version 0.5 + +### API Changes + +- Return type of `vtk2trixi` changed to `NamedTuple` including an optional + `:initial_condition` field if `create_initial_condition=true` is passed. (#959) + ## Version 0.4.3 ### API Changes @@ -27,7 +34,7 @@ used in the Julia ecosystem. Notable changes will be documented in this file for - Added GPU and FP32 support for DEM (#979). - + ### Performance - Improved GPU performance with shifting up to a factor of 10x (#974, #993). @@ -39,8 +46,6 @@ used in the Julia ecosystem. Notable changes will be documented in this file for - Keyword argument `n_clamped_particles` of the `TotalLagrangianSPHSystem` has been deprecated in favor of a new kwarg `clamped_particles`. -- Return type of `vtk2trixi` changed to `NamedTuple` including an optional - `:initial_condition` field if `create_initial_condition=true` is passed. (#959) ### Features @@ -355,5 +360,3 @@ Features: #### TLSPH An implementation of TLSPH (Total Lagrangian Smoothed Particle Hydrodynamics) for solid bodies enabling FSI (Fluid Structure Interactions). - - From 8c50353f57bd3fddd50d298455f402658139307d Mon Sep 17 00:00:00 2001 From: LasNikas Date: Thu, 22 Jan 2026 08:51:26 +0100 Subject: [PATCH 058/134] fix merge bug --- NEWS.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index cad5c3ac24..f24457e7e3 100644 --- a/NEWS.md +++ b/NEWS.md @@ -11,6 +11,12 @@ used in the Julia ecosystem. Notable changes will be documented in this file for - Return type of `vtk2trixi` changed to `NamedTuple` including an optional `:initial_condition` field if `create_initial_condition=true` is passed. (#959) +## Version 0.4.4 + +### Features + +- Added `StateEquationAdaptiveCole` an adaptive sound speed version of the Cole state equation (#875) + ## Version 0.4.3 ### API Changes @@ -34,7 +40,6 @@ used in the Julia ecosystem. Notable changes will be documented in this file for - Added GPU and FP32 support for DEM (#979). - ### Performance - Improved GPU performance with shifting up to a factor of 10x (#974, #993). From 4b3b91177a793b948e5db6ff903e6d23352758fe Mon Sep 17 00:00:00 2001 From: LasNikas Date: Thu, 22 Jan 2026 08:57:18 +0100 Subject: [PATCH 059/134] fix formatting --- src/general/semidiscretization.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/general/semidiscretization.jl b/src/general/semidiscretization.jl index f09231c9e7..85b5e12285 100644 --- a/src/general/semidiscretization.jl +++ b/src/general/semidiscretization.jl @@ -1207,7 +1207,7 @@ function check_configuration(system::ImplicitIncompressibleSPHSystem, systems, n neighbor.boundary_model.density_calculator isa PressureBoundaries) time_step_boundary = neighbor.boundary_model.density_calculator.time_step omega_boundary = neighbor.boundary_model.density_calculator.omega - if !(time_step == time_step_boundary && omega == omega_boundary) + if !(time_step==time_step_boundary && omega==omega_boundary) throw(ArgumentError("`PressureBoundaries` parameters have to be the same as the `ImplicitIncompressibleSPHSystem` parameters")) end From 55635fca26ca776040ee50013e2574180b7af008 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Thu, 22 Jan 2026 09:09:49 +0100 Subject: [PATCH 060/134] better name --- src/general/restart.jl | 4 ++-- src/schemes/boundary/open_boundary/system.jl | 4 ++-- src/schemes/structure/total_lagrangian_sph/system.jl | 5 ++--- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/general/restart.jl b/src/general/restart.jl index e5016ba847..0f0f80dcc6 100644 --- a/src/general/restart.jl +++ b/src/general/restart.jl @@ -29,7 +29,7 @@ function set_initial_conditions!(v0_ode, u0_ode, semi, restart_with::Tuple{Varar v0_system .= Adapt.adapt(semi.parallelization_backend, v_restart) u0_system .= Adapt.adapt(semi.parallelization_backend, u_restart) - precondition_system!(system, restart_file) + restore_previous_state!(system, restart_file) end end @@ -66,7 +66,7 @@ function write_density_and_pressure!(v_restart, system::EntropicallyDampedSPHSys return v_restart end -precondition_system!(system, restart_file) = system +restore_previous_state!(system, restart_file) = system function initialize_neighborhood_searches!(semi, u0_ode, restart_with::Tuple{Vararg{String}}) diff --git a/src/schemes/boundary/open_boundary/system.jl b/src/schemes/boundary/open_boundary/system.jl index c6adac30c1..b63c0f56eb 100644 --- a/src/schemes/boundary/open_boundary/system.jl +++ b/src/schemes/boundary/open_boundary/system.jl @@ -145,7 +145,7 @@ function initialize!(system::OpenBoundarySystem, semi) return system end -# Skip during restart, as boundary zone indices are updated in `precondition_system!` +# Skip during restart, as boundary zone indices are updated in `restore_previous_state!` initialize_restart!(system::OpenBoundarySystem, semi) = system function create_cache_open_boundary(boundary_model, fluid_system, initial_condition, @@ -718,7 +718,7 @@ function restart_v(system::OpenBoundarySystem, data) return v_total end -function precondition_system!(system::OpenBoundarySystem, file) +function restore_previous_state!(system::OpenBoundarySystem, file) # We cannot simply use `update_boundary_zone_indices!` because rounding errors during file I/O # may result in particles being located outside their intended boundary zone, even though they # were written as active particles. diff --git a/src/schemes/structure/total_lagrangian_sph/system.jl b/src/schemes/structure/total_lagrangian_sph/system.jl index 123730ee99..2208ac53d8 100644 --- a/src/schemes/structure/total_lagrangian_sph/system.jl +++ b/src/schemes/structure/total_lagrangian_sph/system.jl @@ -573,13 +573,12 @@ function restart_with!(system::TotalLagrangianSPHSystem, v, u) end function restart_u(system::AbstractStructureSystem, data) - # TODO: Is this correct? - # data.coordinates = coords_integrated + # The integration array requires only the particles that need to be integrated return data.coordinates[:, each_integrated_particle(system)] end function restart_v(system::AbstractStructureSystem, data) - # TODO: Is this correct? + # The integration array requires only the particles that need to be integrated return data.velocity[:, each_integrated_particle(system)] end From f02d72406fb5d8412635753f5e4013db4ddc5cf1 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Thu, 22 Jan 2026 09:36:34 +0100 Subject: [PATCH 061/134] add more tests --- test/general/restart.jl | 110 ++++++++++++++++++++++++++-------------- 1 file changed, 71 insertions(+), 39 deletions(-) diff --git a/test/general/restart.jl b/test/general/restart.jl index 1af3496707..6bc9427547 100644 --- a/test/general/restart.jl +++ b/test/general/restart.jl @@ -1,41 +1,73 @@ @trixi_testset "Restart" begin - # Run full simulation - trixi_include(@__MODULE__, - joinpath(examples_dir(), "fluid", "poiseuille_flow_2d.jl"), - tspan=(0.0, 0.6), sound_speed_factor=10, particle_spacing=4e-5) - - # Since this is an open boundary simulation, the number of active particles may - # differ. The results must be interpolated to enable comparison with the restart - # simulation. The fluid domain starts at `x = 10 * particle_spacing`. - n_interpolation_points = 10 - start_point = [0.0 + 10 * particle_spacing, wall_distance / 2] - end_point = [flow_length - 10 * particle_spacing, wall_distance / 2] - result_full = interpolate_line(start_point, end_point, n_interpolation_points, - semi, fluid_system, sol, cut_off_bnd=false) - - # Run half simulation - trixi_include(@__MODULE__, - joinpath(examples_dir(), "fluid", "poiseuille_flow_2d.jl"), - tspan=(0.0, 0.3), sound_speed_factor=10, particle_spacing=4e-5) - - iter = round(Int, 0.3 / 0.02) - fluid_restart = joinpath("out", "fluid_1_$iter.vtu") - open_boundary_restart = joinpath("out", "open_boundary_1_$iter.vtu") - boundary_restart = joinpath("out", "boundary_1_$iter.vtu") - - ode_restart = semidiscretize(semi, (0.3, 0.6); - restart_with=(fluid_restart, open_boundary_restart, - boundary_restart)) - - sol_restart = solve(ode_restart, RDPK3SpFSAL35(), abstol=1e-5, reltol=1e-3, dtmax=1e-2, - save_everystep=false, callback=UpdateCallback()) - - result_restart = interpolate_line(start_point, end_point, - n_interpolation_points, sol_restart.prob.p, - sol_restart.prob.p.systems[1], - sol_restart, cut_off_bnd=false) - - @test isapprox(result_full.velocity, result_restart.velocity, rtol=8e-3) - @test isapprox(result_full.density, result_restart.density, rtol=8e-4) - @test isapprox(result_full.pressure, result_restart.pressure, rtol=7e-2) + @trixi_testset "Half-Simulation Restart" begin + # Run full simulation + trixi_include(@__MODULE__, + joinpath(examples_dir(), "fluid", "poiseuille_flow_2d.jl"), + tspan=(0.0, 0.6), sound_speed_factor=10, particle_spacing=4e-5) + + # Since this is an open boundary simulation, the number of active particles may + # differ. The results must be interpolated to enable comparison with the restart + # simulation. The fluid domain starts at `x = 10 * particle_spacing`. + n_interpolation_points = 10 + start_point = [0.0 + 10 * particle_spacing, wall_distance / 2] + end_point = [flow_length - 10 * particle_spacing, wall_distance / 2] + result_full = interpolate_line(start_point, end_point, n_interpolation_points, + semi, fluid_system, sol, cut_off_bnd=false) + + # Run half simulation + trixi_include(@__MODULE__, + joinpath(examples_dir(), "fluid", "poiseuille_flow_2d.jl"), + tspan=(0.0, 0.3), sound_speed_factor=10, particle_spacing=4e-5) + + iter = round(Int, 0.3 / 0.02) + fluid_restart = joinpath("out", "fluid_1_$iter.vtu") + open_boundary_restart = joinpath("out", "open_boundary_1_$iter.vtu") + boundary_restart = joinpath("out", "boundary_1_$iter.vtu") + + ode_restart = semidiscretize(semi, (0.3, 0.6); + restart_with=(fluid_restart, open_boundary_restart, + boundary_restart)) + + sol_restart = solve(ode_restart, RDPK3SpFSAL35(), abstol=1e-5, reltol=1e-3, + dtmax=1e-2, save_everystep=false, callback=UpdateCallback()) + + result_restart = interpolate_line(start_point, end_point, + n_interpolation_points, sol_restart.prob.p, + sol_restart.prob.p.systems[1], + sol_restart, cut_off_bnd=false) + + @test isapprox(result_full.velocity, result_restart.velocity, rtol=8e-3) + @test isapprox(result_full.density, result_restart.density, rtol=8e-4) + @test isapprox(result_full.pressure, result_restart.pressure, rtol=7e-2) + end + + @trixi_testset "Restore Previous State" begin + R1 = 1.7714 + R2 = 106.66 + C = 1.1808e-2 + pressure_model = RCRWindkesselModel(; peripheral_resistance=R2, + compliance=C, + characteristic_resistance=R1) + # Run half simulation + trixi_include(@__MODULE__, + joinpath(examples_dir(), "fluid", "poiseuille_flow_2d.jl"), + reference_pressure_out=pressure_model, + tspan=(0.0, 0.3), sound_speed_factor=10, particle_spacing=4e-5) + + iter = round(Int, 0.3 / 0.02) + fluid_restart = joinpath("out", "fluid_1_$iter.vtu") + open_boundary_restart = joinpath("out", "open_boundary_1_$iter.vtu") + boundary_restart = joinpath("out", "boundary_1_$iter.vtu") + + ode_restart = semidiscretize(semi, (0.3, 0.6); + restart_with=(fluid_restart, open_boundary_restart, + boundary_restart)) + restart_pressure = ode_restart.p.systems[2].cache.pressure_reference_values[2].pressure[] + restart_flow_rate = ode_restart.p.systems[2].cache.pressure_reference_values[2].flow_rate[] + + @test isapprox(restart_pressure, + open_boundary.cache.pressure_reference_values[2].pressure[]) + @test isapprox(restart_flow_rate, + open_boundary.cache.pressure_reference_values[2].flow_rate[]) + end end From 6221cbc173bed0e3a99ff9394331d6d6f9badd4a Mon Sep 17 00:00:00 2001 From: LasNikas Date: Fri, 17 Apr 2026 14:52:15 +0200 Subject: [PATCH 062/134] revise example --- .../postprocessing/restart_poiseuille_flow_2d.jl | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/examples/postprocessing/restart_poiseuille_flow_2d.jl b/examples/postprocessing/restart_poiseuille_flow_2d.jl index fdde06d4b4..0332a1c31d 100644 --- a/examples/postprocessing/restart_poiseuille_flow_2d.jl +++ b/examples/postprocessing/restart_poiseuille_flow_2d.jl @@ -1,21 +1,18 @@ # ========================================================================================== -# Restart Example +# Restart Example: Poiseuille Flow 2D # -# TODO +# This example demonstrates how to restart a simulation. +# We first run a simulation of 2D Poiseuille flow up to t=0.3s, then restart from the +# saved state and continue the simulation until t=0.6s. # ========================================================================================== using TrixiParticles -# Run full simulation -# trixi_include(@__MODULE__, -# joinpath(examples_dir(), "fluid", "poiseuille_flow_2d.jl"), -# tspan=(0.0, 0.6), sound_speed_factor=10, particle_spacing=4e-5) - -# Run half simulation trixi_include(@__MODULE__, joinpath(examples_dir(), "fluid", "poiseuille_flow_2d.jl"), tspan=(0.0, 0.3), sound_speed_factor=10, particle_spacing=4e-5) -iter = round(Int, 0.3 / 0.02) +# Get latest iteration +iter = saving_callback.condition.index[] - 1 restart_file_fluid = joinpath("out", "fluid_1_$iter.vtu") restart_file_open_boundary = joinpath("out", "open_boundary_1_$iter.vtu") From 8ba5006183eb8c8d7af3d6cc2108a7224b2dc1e7 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Wed, 13 May 2026 11:49:03 +0200 Subject: [PATCH 063/134] implement suggestions --- src/general/restart.jl | 10 ++++++++-- src/general/semidiscretization.jl | 13 ++++++++++--- src/io/read_vtk.jl | 2 +- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/general/restart.jl b/src/general/restart.jl index 0f0f80dcc6..60902f2279 100644 --- a/src/general/restart.jl +++ b/src/general/restart.jl @@ -34,8 +34,14 @@ function set_initial_conditions!(v0_ode, u0_ode, semi, restart_with::Tuple{Varar end function time_span(tspan, restart_with::Tuple{Vararg{String}}) - restart_data = vtk2trixi(first(restart_with)) - t_restart = convert(eltype(tspan), restart_data.time) + # Read restart times from all files + restart_times = [vtk2trixi(file).time for file in restart_with] + t_restart = convert(eltype(tspan), first(restart_times)) + + # Check if all restart files have the same time + if !all(isapprox(t, t_restart) for t in restart_times) + throw(ArgumentError("All restart files must start from the same time.")) + end if !isapprox(tspan[1], t_restart) @info "Adjusting initial time from $(tspan[1]) to restart time $t_restart" diff --git a/src/general/semidiscretization.jl b/src/general/semidiscretization.jl index 123e5d4675..e02ae87782 100644 --- a/src/general/semidiscretization.jl +++ b/src/general/semidiscretization.jl @@ -194,9 +194,9 @@ Create an `ODEProblem` from the semidiscretization with the specified `tspan`. # Keywords - `restart_with`: Can be used to restart the simulation from VTK solution files (see [`SolutionSavingCallback`](@ref)). - This has to be a tuple of filenames, one for each system in the [`Semidiscretization`](@ref). - The order of the filenames has to match the order of the systems in the [`Semidiscretization`](@ref). - If no restart is desired, use `nothing` (default). + This can be either `nothing` (default, no restart), a single filename as a `String`, + or a `Tuple` of filenames, one for each system in the [`Semidiscretization`](@ref). + The order of the filenames must match the order of the systems in the [`Semidiscretization`](@ref). - `reset_threads`: A boolean flag to reset Polyester.jl threads before the simulation (default: `true`). After an error within a threaded loop, threading might be disabled. Resetting the threads before the simulation ensures that threading is enabled again for the simulation. @@ -226,6 +226,13 @@ u0: ([...], [...]) *this line is ignored by filter* function semidiscretize(semi, tspan; reset_threads=true, restart_with=nothing) (; systems) = semi + if restart_with isa String + restart_with = (restart_with,) + elseif !isnothing(restart_with) && !(restart_with isa Tuple) + throw(ArgumentError("`restart_with` must be `nothing`, a string, or a tuple of strings, " * + "got $(typeof(restart_with))")) + end + # Check that all systems have the same eltype first_system = first(systems) if !all(system -> eltype(system) === eltype(first_system), systems) diff --git a/src/io/read_vtk.jl b/src/io/read_vtk.jl index 86bae1af77..0d3d54b13d 100644 --- a/src/io/read_vtk.jl +++ b/src/io/read_vtk.jl @@ -52,7 +52,7 @@ function vtk2trixi(file; element_type=nothing, coordinates_eltype=nothing, cELTYPE = isnothing(coordinates_eltype) ? eltype(point_coords) : coordinates_eltype ELTYPE = isnothing(element_type) ? - eltype(first(ReadVTK.get_data(point_data["pressure"]))) : element_type + eltype(first(ReadVTK.get_data(point_data["particle_spacing"]))) : element_type results = Dict{Symbol, Any}() From 393b6443e16f34a8661f450a2c9d93bf2cab1674 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Wed, 13 May 2026 11:49:12 +0200 Subject: [PATCH 064/134] add structure example --- .../restart_oscillating_beam_2d.jl | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 examples/postprocessing/restart_oscillating_beam_2d.jl diff --git a/examples/postprocessing/restart_oscillating_beam_2d.jl b/examples/postprocessing/restart_oscillating_beam_2d.jl new file mode 100644 index 0000000000..caa5f51da4 --- /dev/null +++ b/examples/postprocessing/restart_oscillating_beam_2d.jl @@ -0,0 +1,27 @@ +# ========================================================================================== +# Restart Example: Poiseuille Flow 2D +# +# This example demonstrates how to restart a simulation. +# We first run a simulation of oscillating beam up to t=1.0s, then restart from the +# saved state and continue the simulation until t=2.0s. +# ========================================================================================== +using TrixiParticles + +trixi_include(@__MODULE__, + joinpath(examples_dir(), "structure", "oscillating_beam_2d.jl"), + tspan=(0.0, 1.0)) + +# Get latest iteration +iter = saving_callback.affect!.affect!.latest_saved_iter + +restart_file = joinpath("out", "structure_1_$iter.vtu") + +ode_restart = semidiscretize(semi, (1.0, 2.0); + restart_with=restart_file) + +saving_callback = SolutionSavingCallback(dt=0.02, prefix="restart") + +callbacks = CallbackSet(info_callback, saving_callback) + +sol_restart = solve(ode_restart, RDPK3SpFSAL35(), abstol=1e-5, reltol=1e-3, dtmax=1e-2, + save_everystep=false, callback=callbacks) From efeb666daa2c243553fdb43d3e41cdb2ee562aaa Mon Sep 17 00:00:00 2001 From: LasNikas Date: Wed, 13 May 2026 11:54:38 +0200 Subject: [PATCH 065/134] add tests --- test/general/restart.jl | 39 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/test/general/restart.jl b/test/general/restart.jl index 386038e6b1..6783352d1b 100644 --- a/test/general/restart.jl +++ b/test/general/restart.jl @@ -1,5 +1,5 @@ @trixi_testset "Restart" begin - @trixi_testset "Half-Simulation Restart" begin + @trixi_testset "Poiseuille Flow Half-Simulation Restart" begin # Run full simulation trixi_include(@__MODULE__, joinpath(examples_dir(), "fluid", "poiseuille_flow_2d.jl"), @@ -41,7 +41,7 @@ @test isapprox(result_full.pressure, result_restart.pressure, rtol=8e-2) end - @trixi_testset "Restore Previous State" begin + @trixi_testset "Poiseuille Flow Restore Previous State" begin R1 = 1.7714 R2 = 106.66 C = 1.1808e-2 @@ -70,4 +70,39 @@ @test isapprox(restart_flow_rate, open_boundary.cache.pressure_reference_values[2].flow_rate[]) end + + @trixi_testset "Oscillating Beam Half-Simulation Restart" begin + # Run full simulation + trixi_include(@__MODULE__, + joinpath(examples_dir(), "structure", "oscillating_beam_2d.jl"), + tspan=(0.0, 1.0), n_particles_y=5) + + # Store the final solution for comparison + full_sol = sol + full_final_velocities = copy(full_sol.u[end]) + + # Run half simulation + trixi_include(@__MODULE__, + joinpath(examples_dir(), "structure", "oscillating_beam_2d.jl"), + tspan=(0.0, 0.5), n_particles_y=5) + + # Get latest iteration and create restart + iter = saving_callback.affect!.affect!.latest_saved_iter + restart_file = joinpath("out", "structure_1_$iter.vtu") + + ode_restart = semidiscretize(semi, (0.5, 1.0); + restart_with=restart_file) + + # Create a new saving callback for the restart + saving_callback_restart = SolutionSavingCallback(dt=0.02, prefix="restart"; + deflection_x, deflection_y) + info_callback_restart = InfoCallback(interval=1000) + callbacks_restart = CallbackSet(info_callback_restart, saving_callback_restart) + + sol_restart = solve(ode_restart, RDPK3SpFSAL49(), save_everystep=false, + callback=callbacks_restart) + + # Compare the final particle velocities + @test isapprox(sol_restart.u[end], full_final_velocities, rtol=1e-3) + end end From a95dd226c573a9ea1b78d6e8a6f259377680d0b4 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Wed, 13 May 2026 11:56:20 +0200 Subject: [PATCH 066/134] fix description --- examples/postprocessing/restart_oscillating_beam_2d.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/postprocessing/restart_oscillating_beam_2d.jl b/examples/postprocessing/restart_oscillating_beam_2d.jl index caa5f51da4..275c77236c 100644 --- a/examples/postprocessing/restart_oscillating_beam_2d.jl +++ b/examples/postprocessing/restart_oscillating_beam_2d.jl @@ -1,5 +1,5 @@ # ========================================================================================== -# Restart Example: Poiseuille Flow 2D +# Restart Example: Oscillting Beam 2D # # This example demonstrates how to restart a simulation. # We first run a simulation of oscillating beam up to t=1.0s, then restart from the From d05f19868e1a1ce598104d519d749b2030a8ab7e Mon Sep 17 00:00:00 2001 From: LasNikas Date: Wed, 13 May 2026 12:25:04 +0200 Subject: [PATCH 067/134] fix tests --- src/io/read_vtk.jl | 18 +++++++++++++++--- test/general/restart.jl | 8 ++++---- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/src/io/read_vtk.jl b/src/io/read_vtk.jl index 0d3d54b13d..a14096ea99 100644 --- a/src/io/read_vtk.jl +++ b/src/io/read_vtk.jl @@ -51,8 +51,19 @@ function vtk2trixi(file; element_type=nothing, coordinates_eltype=nothing, point_coords = ReadVTK.get_points(vtk_file) cELTYPE = isnothing(coordinates_eltype) ? eltype(point_coords) : coordinates_eltype - ELTYPE = isnothing(element_type) ? - eltype(first(ReadVTK.get_data(point_data["particle_spacing"]))) : element_type + + if !isnothing(element_type) + ELTYPE = element_type + else + # Try to get element type from pressure ore density (whichever exists) + ELTYPE = if "pressure" in keys(point_data) + eltype(first(ReadVTK.get_data(point_data["pressure"]))) + elseif "material_density" in keys(point_data) + eltype(first(ReadVTK.get_data(point_data["material_density"]))) + else + error("Neither 'pressure' nor 'material_density' field found in VTK file") + end + end results = Dict{Symbol, Any}() @@ -87,7 +98,8 @@ function vtk2trixi(file; element_type=nothing, coordinates_eltype=nothing, results[:particle_spacing] results[:coordinates] = coordinates results[:time] = "time" in keys(field_data) ? - first(ReadVTK.get_data(field_data["time"])) : zero(ELTYPE) + convert.(ELTYPE, first(ReadVTK.get_data(field_data["time"]))) : + zero(ELTYPE) append!(used_keys, ["index", "ndims"]) # Load any custom quantities diff --git a/test/general/restart.jl b/test/general/restart.jl index 6783352d1b..8692939cb3 100644 --- a/test/general/restart.jl +++ b/test/general/restart.jl @@ -32,8 +32,8 @@ dtmax=1e-2, save_everystep=false, callback=UpdateCallback()) result_restart = interpolate_line(start_point, end_point, - n_interpolation_points, sol_restart.prob.p, - sol_restart.prob.p.systems[1], + n_interpolation_points, sol_restart.prob.p.semi, + sol_restart.prob.p.semi.systems[1], sol_restart, cut_off_bnd=false) @test isapprox(result_full.velocity, result_restart.velocity, rtol=1e-2) @@ -62,8 +62,8 @@ ode_restart = semidiscretize(semi, (0.3, 0.6); restart_with=(fluid_restart, open_boundary_restart, boundary_restart)) - restart_pressure = ode_restart.p.systems[2].cache.pressure_reference_values[2].pressure[] - restart_flow_rate = ode_restart.p.systems[2].cache.pressure_reference_values[2].flow_rate[] + restart_pressure = ode_restart.p.semi.systems[2].cache.pressure_reference_values[2].pressure[] + restart_flow_rate = ode_restart.p.semi.systems[2].cache.pressure_reference_values[2].flow_rate[] @test isapprox(restart_pressure, open_boundary.cache.pressure_reference_values[2].pressure[]) From c92869cf7bf2fb50ef60cbf9c28a93dd58003d09 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Wed, 13 May 2026 12:28:17 +0200 Subject: [PATCH 068/134] avoid outputs --- test/general/restart.jl | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/test/general/restart.jl b/test/general/restart.jl index 8692939cb3..1009d48356 100644 --- a/test/general/restart.jl +++ b/test/general/restart.jl @@ -3,7 +3,8 @@ # Run full simulation trixi_include(@__MODULE__, joinpath(examples_dir(), "fluid", "poiseuille_flow_2d.jl"), - tspan=(0.0, 0.6), sound_speed_factor=10, particle_spacing=4e-5) + tspan=(0.0, 0.6), sound_speed_factor=10, particle_spacing=4e-5, + info_callback=nothing) # Since this is an open boundary simulation, the number of active particles may # differ. The results must be interpolated to enable comparison with the restart @@ -17,7 +18,8 @@ # Run half simulation trixi_include(@__MODULE__, joinpath(examples_dir(), "fluid", "poiseuille_flow_2d.jl"), - tspan=(0.0, 0.3), sound_speed_factor=10, particle_spacing=4e-5) + tspan=(0.0, 0.3), sound_speed_factor=10, particle_spacing=4e-5, + info_callback=nothing) iter = round(Int, 0.3 / 0.02) fluid_restart = joinpath("out", "fluid_1_$iter.vtu") @@ -51,7 +53,7 @@ # Run half simulation trixi_include(@__MODULE__, joinpath(examples_dir(), "fluid", "poiseuille_flow_2d.jl"), - outlet_reference_pressure=pressure_model, + outlet_reference_pressure=pressure_model, info_callback=nothing, tspan=(0.0, 0.3), sound_speed_factor=10, particle_spacing=4e-5) iter = round(Int, 0.3 / 0.02) @@ -75,7 +77,7 @@ # Run full simulation trixi_include(@__MODULE__, joinpath(examples_dir(), "structure", "oscillating_beam_2d.jl"), - tspan=(0.0, 1.0), n_particles_y=5) + tspan=(0.0, 1.0), n_particles_y=5, info_callback=nothing) # Store the final solution for comparison full_sol = sol @@ -96,8 +98,7 @@ # Create a new saving callback for the restart saving_callback_restart = SolutionSavingCallback(dt=0.02, prefix="restart"; deflection_x, deflection_y) - info_callback_restart = InfoCallback(interval=1000) - callbacks_restart = CallbackSet(info_callback_restart, saving_callback_restart) + callbacks_restart = CallbackSet(saving_callback_restart) sol_restart = solve(ode_restart, RDPK3SpFSAL49(), save_everystep=false, callback=callbacks_restart) From 64d41c4c1dab9fbc00f35f748607325ef3010226 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Wed, 13 May 2026 13:39:27 +0200 Subject: [PATCH 069/134] add NEWS entry --- NEWS.md | 28 ++++++++++++++++------------ test/general/restart.jl | 4 ++-- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/NEWS.md b/NEWS.md index 6c8b435022..cd9a24fcd8 100644 --- a/NEWS.md +++ b/NEWS.md @@ -2,18 +2,22 @@ TrixiParticles.jl follows the interpretation of [semantic versioning (semver)](https://julialang.github.io/Pkg.jl/dev/compatibility/#Version-specifier-format-1) -used in the Julia ecosystem. Notable changes will be documented in this file for human readability. - -## Version 0.5.1 - -### Important Bugfixes - -Fix empty PVD collections in `SolutionSavingCallback` output and incorrect metadata -when using `save_times`. - -## Version 0.5 - -### API Changes +used in the Julia ecosystem. Notable changes will be documented in this file for human readability. + +## Version 0.5.1 + +### Features +- Added the ability to restart simulations from VTK solution files generated by `SolutionSavingCallback`. + Users can now pass the `restart_with` keyword argument to `semidiscretize` (#1190). + +### Important Bugfixes + +Fix empty PVD collections in `SolutionSavingCallback` output and incorrect metadata +when using `save_times`. + +## Version 0.5 + +### API Changes - Clipping of negative pressure values in the `BoundaryModelDummyParticles` is now disabled by default and can be enabled with the keyword argument `clip_negative_pressure=true` (#1143). diff --git a/test/general/restart.jl b/test/general/restart.jl index 1009d48356..fc0c62b9aa 100644 --- a/test/general/restart.jl +++ b/test/general/restart.jl @@ -81,7 +81,7 @@ # Store the final solution for comparison full_sol = sol - full_final_velocities = copy(full_sol.u[end]) + positions_full = full_sol.u[end].x[2] # Run half simulation trixi_include(@__MODULE__, @@ -104,6 +104,6 @@ callback=callbacks_restart) # Compare the final particle velocities - @test isapprox(sol_restart.u[end], full_final_velocities, rtol=1e-3) + @test isapprox(sol_restart.u[end].x[2], positions_full, rtol=1e-6) end end From b80727d9adf0cc098f5b94a41ca2d40759745040 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Mon, 1 Jun 2026 11:57:26 +0200 Subject: [PATCH 070/134] implement suggestions --- src/general/restart.jl | 8 ++++++++ src/schemes/structure/total_lagrangian_sph/system.jl | 4 ++-- test/examples/gpu.jl | 6 +++--- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/general/restart.jl b/src/general/restart.jl index 60902f2279..3d9bfe0f10 100644 --- a/src/general/restart.jl +++ b/src/general/restart.jl @@ -72,6 +72,14 @@ function write_density_and_pressure!(v_restart, system::EntropicallyDampedSPHSys return v_restart end +function write_density_and_pressure!(v_restart, system::EntropicallyDampedSPHSystem, + density_calculator::SummationDensity, + pressure, density) + v_restart[size(v_restart, 1) - 1, :] = pressure + + return v_restart +end + restore_previous_state!(system, restart_file) = system function initialize_neighborhood_searches!(semi, u0_ode, diff --git a/src/schemes/structure/total_lagrangian_sph/system.jl b/src/schemes/structure/total_lagrangian_sph/system.jl index 81b2561447..ae7029fc4d 100644 --- a/src/schemes/structure/total_lagrangian_sph/system.jl +++ b/src/schemes/structure/total_lagrangian_sph/system.jl @@ -630,12 +630,12 @@ function restart_with!(system::TotalLagrangianSPHSystem, v, u) restart_with!(system, system.boundary_model, v, u) end -function restart_u(system::AbstractStructureSystem, data) +function restart_u(system::TotalLagrangianSPHSystem, data) # The integration array requires only the particles that need to be integrated return data.coordinates[:, each_integrated_particle(system)] end -function restart_v(system::AbstractStructureSystem, data) +function restart_v(system::TotalLagrangianSPHSystem, data) # The integration array requires only the particles that need to be integrated return data.velocity[:, each_integrated_particle(system)] end diff --git a/test/examples/gpu.jl b/test/examples/gpu.jl index 1653a13fc1..b8bcc06d36 100644 --- a/test/examples/gpu.jl +++ b/test/examples/gpu.jl @@ -913,7 +913,7 @@ end start_point = [0.0f0 + 10 * particle_spacing, wall_distance / 2] end_point = [flow_length - 10 * particle_spacing, wall_distance / 2] result_full = interpolate_line(start_point, end_point, n_interpolation_points, - sol.prob.p, sol.prob.p.systems[1], sol, + sol.prob.p.semi, sol.prob.p.semi.systems[1], sol, cut_off_bnd=false) # Run half simulation and safe checkpoint @@ -926,14 +926,14 @@ end parallelization_backend=Main.parallelization_backend) # Transfer `semi` back to CPU - semi_new = TrixiParticles.Adapt.adapt(Array, sol.prob.p) + semi_cpu = TrixiParticles.Adapt.adapt(Array, sol.prob.p.semi) iter = round(Int, 0.3 / 0.02) fluid_restart = joinpath("out", "fluid_1_$iter.vtu") open_boundary_restart = joinpath("out", "open_boundary_1_$iter.vtu") boundary_restart = joinpath("out", "boundary_1_$iter.vtu") - ode_restart = semidiscretize(semi_new, (0.3f0, 0.6f0); + ode_restart = semidiscretize(semi_cpu, (0.3f0, 0.6f0); restart_with=(fluid_restart, open_boundary_restart, boundary_restart)) From ed07cfb3c94db5a3d207522271966aa5d5d27ebc Mon Sep 17 00:00:00 2001 From: LasNikas Date: Tue, 16 Jun 2026 11:37:53 +0200 Subject: [PATCH 071/134] implement suggestions --- .../restart_oscillating_beam_2d.jl | 2 +- src/general/restart.jl | 2 +- src/general/semidiscretization.jl | 6 +++++- src/io/read_vtk.jl | 2 +- src/schemes/boundary/open_boundary/system.jl | 6 ++++++ src/schemes/fluid/fluid.jl | 17 ++++++++++------- test/examples/gpu.jl | 10 +++++----- test/general/restart.jl | 4 ++-- 8 files changed, 31 insertions(+), 18 deletions(-) diff --git a/examples/postprocessing/restart_oscillating_beam_2d.jl b/examples/postprocessing/restart_oscillating_beam_2d.jl index 275c77236c..51080ec265 100644 --- a/examples/postprocessing/restart_oscillating_beam_2d.jl +++ b/examples/postprocessing/restart_oscillating_beam_2d.jl @@ -1,5 +1,5 @@ # ========================================================================================== -# Restart Example: Oscillting Beam 2D +# Restart Example: Oscillating Beam 2D # # This example demonstrates how to restart a simulation. # We first run a simulation of oscillating beam up to t=1.0s, then restart from the diff --git a/src/general/restart.jl b/src/general/restart.jl index 3d9bfe0f10..33efd006ec 100644 --- a/src/general/restart.jl +++ b/src/general/restart.jl @@ -1,7 +1,7 @@ function set_initial_conditions!(v0_ode, u0_ode, semi, restart_with::Tuple{Vararg{String}}) # Check number of systems if length(semi.systems) != length(restart_with) - throw(ArgumentError("Number of systems in `semi` does not match number of restart files provided " * + throw(ArgumentError("number of systems in `semi` does not match number of restart files provided " * "in `restart_with`")) end diff --git a/src/general/semidiscretization.jl b/src/general/semidiscretization.jl index 7e42b515d3..514f1e6b9f 100644 --- a/src/general/semidiscretization.jl +++ b/src/general/semidiscretization.jl @@ -197,6 +197,10 @@ Create an `ODEProblem` from the semidiscretization with the specified `tspan`. This can be either `nothing` (default, no restart), a single filename as a `String`, or a `Tuple` of filenames, one for each system in the [`Semidiscretization`](@ref). The order of the filenames must match the order of the systems in the [`Semidiscretization`](@ref). + Note that `semidiscretize` replaces the initial time (`tspan[1]`) with the timestamp read + from the VTK files. If the user-provided `tspan[1]` does not match the restart time, + it is adjusted and an info message is logged. If multiple files are provided, their + timestamps must match. - `reset_threads`: A boolean flag to reset Polyester.jl threads before the simulation (default: `true`). After an error within a threaded loop, threading might be disabled. Resetting the threads before the simulation ensures that threading is enabled again for the simulation. @@ -228,7 +232,7 @@ function semidiscretize(semi, tspan; reset_threads=true, restart_with=nothing) if restart_with isa String restart_with = (restart_with,) - elseif !isnothing(restart_with) && !(restart_with isa Tuple) + elseif !isnothing(restart_with) && !(restart_with isa NTuple{<:Any, String}) throw(ArgumentError("`restart_with` must be `nothing`, a string, or a tuple of strings, " * "got $(typeof(restart_with))")) end diff --git a/src/io/read_vtk.jl b/src/io/read_vtk.jl index a14096ea99..aefa4a3c72 100644 --- a/src/io/read_vtk.jl +++ b/src/io/read_vtk.jl @@ -55,7 +55,7 @@ function vtk2trixi(file; element_type=nothing, coordinates_eltype=nothing, if !isnothing(element_type) ELTYPE = element_type else - # Try to get element type from pressure ore density (whichever exists) + # Try to get element type from pressure or density (whichever exists) ELTYPE = if "pressure" in keys(point_data) eltype(first(ReadVTK.get_data(point_data["pressure"]))) elseif "material_density" in keys(point_data) diff --git a/src/schemes/boundary/open_boundary/system.jl b/src/schemes/boundary/open_boundary/system.jl index ffe3c97782..4348f7df6f 100644 --- a/src/schemes/boundary/open_boundary/system.jl +++ b/src/schemes/boundary/open_boundary/system.jl @@ -715,6 +715,9 @@ function restart_u(system::OpenBoundarySystem, data) n_integrated_particles(system)) coords_total .= coordinates_eltype(system)(1e16) + # Since only active particles are written during saving, the loaded `data` contains + # only active particles. These are placed at the beginning of the array, leaving the + # inactive buffer particles at the end. Thus, we can safely activate the first N particles. coords_active = data.coordinates for particle in axes(coords_active, 2) for dim in 1:ndims(system) @@ -734,6 +737,9 @@ function restart_v(system::OpenBoundarySystem, data) v_total = zeros(eltype(system), v_nvariables(system), n_integrated_particles(system)) + # Since only active particles are written during saving, the loaded `data` contains + # only active particles. These are placed at the beginning of the array, leaving the + # inactive buffer particles at the end. Thus, we can safely activate the first N particles. v_active = zeros(eltype(system), v_nvariables(system), size(data.velocity, 2)) v_active[1:ndims(system), :] = data.velocity diff --git a/src/schemes/fluid/fluid.jl b/src/schemes/fluid/fluid.jl index be9c7ac6dd..53293df2c6 100644 --- a/src/schemes/fluid/fluid.jl +++ b/src/schemes/fluid/fluid.jl @@ -266,13 +266,13 @@ function restart_u(system::AbstractFluidSystem, data) end end - if !isnothing(buffer(system)) - system.buffer.active_particle .= false - system.buffer.active_particle[1:size(coords_active, 2)] .= true + buf = buffer(system) + if !isnothing(buf) + buf.active_particle .= false + buf.active_particle[1:size(coords_active, 2)] .= true + update_system_buffer!(buf) end - update_system_buffer!(system.buffer) - return coords_total end @@ -282,8 +282,11 @@ function restart_v(system::AbstractFluidSystem, data) velocity_active = zeros(eltype(system), v_nvariables(system), size(data.velocity, 2)) velocity_active[1:ndims(system), :] = data.velocity - write_density_and_pressure!(velocity_active, system, density_calculator(system), - data.pressure, data.density) + + density_calc = hasproperty(system, :density_calculator) ? density_calculator(system) : + nothing + write_density_and_pressure!(velocity_active, system, density_calc, data.pressure, + data.density) for particle in axes(velocity_active, 2) for i in axes(velocity_active, 1) diff --git a/test/examples/gpu.jl b/test/examples/gpu.jl index b8bcc06d36..a70216110c 100644 --- a/test/examples/gpu.jl +++ b/test/examples/gpu.jl @@ -910,8 +910,8 @@ end # differ. The results must be interpolated to enable comparison with the restart # simulation. The fluid domain starts at `x = 10 * particle_spacing`. n_interpolation_points = 10 - start_point = [0.0f0 + 10 * particle_spacing, wall_distance / 2] - end_point = [flow_length - 10 * particle_spacing, wall_distance / 2] + start_point = [0.0f0 + 10 * particle_spacing, domain_size[1] / 2] + end_point = [domain_size[2] - 10 * particle_spacing, domain_size[1] / 2] result_full = interpolate_line(start_point, end_point, n_interpolation_points, sol.prob.p.semi, sol.prob.p.semi.systems[1], sol, cut_off_bnd=false) @@ -928,7 +928,7 @@ end # Transfer `semi` back to CPU semi_cpu = TrixiParticles.Adapt.adapt(Array, sol.prob.p.semi) - iter = round(Int, 0.3 / 0.02) + iter = saving_callback.affect!.affect!.latest_saved_iter fluid_restart = joinpath("out", "fluid_1_$iter.vtu") open_boundary_restart = joinpath("out", "open_boundary_1_$iter.vtu") boundary_restart = joinpath("out", "boundary_1_$iter.vtu") @@ -942,8 +942,8 @@ end callback=UpdateCallback()) result_restart = interpolate_line(start_point, end_point, - n_interpolation_points, sol_restart.prob.p, - sol_restart.prob.p.systems[1], + n_interpolation_points, sol_restart.prob.p.semi, + sol_restart.prob.p.semi.systems[1], sol_restart, cut_off_bnd=false) @test isapprox(result_full.velocity, result_restart.velocity, rtol=8e-3) diff --git a/test/general/restart.jl b/test/general/restart.jl index fc0c62b9aa..21e627e8e6 100644 --- a/test/general/restart.jl +++ b/test/general/restart.jl @@ -21,7 +21,7 @@ tspan=(0.0, 0.3), sound_speed_factor=10, particle_spacing=4e-5, info_callback=nothing) - iter = round(Int, 0.3 / 0.02) + iter = saving_callback.affect!.affect!.latest_saved_iter fluid_restart = joinpath("out", "fluid_1_$iter.vtu") open_boundary_restart = joinpath("out", "open_boundary_1_$iter.vtu") boundary_restart = joinpath("out", "boundary_1_$iter.vtu") @@ -56,7 +56,7 @@ outlet_reference_pressure=pressure_model, info_callback=nothing, tspan=(0.0, 0.3), sound_speed_factor=10, particle_spacing=4e-5) - iter = round(Int, 0.3 / 0.02) + iter = saving_callback.affect!.affect!.latest_saved_iter fluid_restart = joinpath("out", "fluid_1_$iter.vtu") open_boundary_restart = joinpath("out", "open_boundary_1_$iter.vtu") boundary_restart = joinpath("out", "boundary_1_$iter.vtu") From adc70a6441997355db133663b577a65ac9cc11a6 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Tue, 16 Jun 2026 12:14:27 +0200 Subject: [PATCH 072/134] fix gpu --- test/examples/gpu.jl | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/test/examples/gpu.jl b/test/examples/gpu.jl index a70216110c..ce7a000a59 100644 --- a/test/examples/gpu.jl +++ b/test/examples/gpu.jl @@ -911,7 +911,7 @@ end # simulation. The fluid domain starts at `x = 10 * particle_spacing`. n_interpolation_points = 10 start_point = [0.0f0 + 10 * particle_spacing, domain_size[1] / 2] - end_point = [domain_size[2] - 10 * particle_spacing, domain_size[1] / 2] + end_point = [domain_size[1] - 10 * particle_spacing, domain_size[2] / 2] result_full = interpolate_line(start_point, end_point, n_interpolation_points, sol.prob.p.semi, sol.prob.p.semi.systems[1], sol, cut_off_bnd=false) @@ -925,15 +925,12 @@ end coordinates_eltype=Float32, parallelization_backend=Main.parallelization_backend) - # Transfer `semi` back to CPU - semi_cpu = TrixiParticles.Adapt.adapt(Array, sol.prob.p.semi) - iter = saving_callback.affect!.affect!.latest_saved_iter fluid_restart = joinpath("out", "fluid_1_$iter.vtu") open_boundary_restart = joinpath("out", "open_boundary_1_$iter.vtu") boundary_restart = joinpath("out", "boundary_1_$iter.vtu") - ode_restart = semidiscretize(semi_cpu, (0.3f0, 0.6f0); + ode_restart = semidiscretize(semi, (0.3f0, 0.6f0); restart_with=(fluid_restart, open_boundary_restart, boundary_restart)) @@ -946,8 +943,8 @@ end sol_restart.prob.p.semi.systems[1], sol_restart, cut_off_bnd=false) - @test isapprox(result_full.velocity, result_restart.velocity, rtol=8e-3) - @test isapprox(result_full.density, result_restart.density, rtol=8e-4) - @test isapprox(result_full.pressure, result_restart.pressure, rtol=8e-2) + @test isapprox(result_full.velocity, result_restart.velocity, rtol=8f-3) + @test isapprox(result_full.density, result_restart.density, rtol=8f-4) + @test isapprox(result_full.pressure, result_restart.pressure, rtol=8f-2) end end From 4bcdddaea1b0ef484a70cf174c2f84a8d0a3a822 Mon Sep 17 00:00:00 2001 From: LasNikas Date: Tue, 16 Jun 2026 12:20:08 +0200 Subject: [PATCH 073/134] apply formatter --- test/examples/gpu.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/examples/gpu.jl b/test/examples/gpu.jl index ce7a000a59..73e66813fc 100644 --- a/test/examples/gpu.jl +++ b/test/examples/gpu.jl @@ -943,8 +943,8 @@ end sol_restart.prob.p.semi.systems[1], sol_restart, cut_off_bnd=false) - @test isapprox(result_full.velocity, result_restart.velocity, rtol=8f-3) - @test isapprox(result_full.density, result_restart.density, rtol=8f-4) - @test isapprox(result_full.pressure, result_restart.pressure, rtol=8f-2) + @test isapprox(result_full.velocity, result_restart.velocity, rtol=8.0f-3) + @test isapprox(result_full.density, result_restart.density, rtol=8.0f-4) + @test isapprox(result_full.pressure, result_restart.pressure, rtol=8.0f-2) end end From 81d595dec0734c58bc150c895854ac3a4e4e5d33 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Mon, 1 Jun 2026 11:22:10 +0200 Subject: [PATCH 074/134] Convert `MechanicalWorkCalculatorCallback` to a custom quantity --- docs/src/callbacks.md | 10 + src/TrixiParticles.jl | 3 +- src/callbacks/callbacks.jl | 1 - src/general/general.jl | 1 + .../mechanical_work_calculator.jl | 172 +++++++----------- test/callbacks/mechanical_work_calculator.jl | 100 +++++----- test/examples/examples.jl | 37 ++-- 7 files changed, 138 insertions(+), 186 deletions(-) rename src/{callbacks => general}/mechanical_work_calculator.jl (50%) diff --git a/docs/src/callbacks.md b/docs/src/callbacks.md index c96ccc7dba..e356917226 100644 --- a/docs/src/callbacks.md +++ b/docs/src/callbacks.md @@ -14,3 +14,13 @@ The following pre-defined custom quantities can be used with the Modules = [TrixiParticles] Pages = ["general/custom_quantities.jl"] ``` + +# Mechanical Work Calculator + +The `MechanicalWorkCalculator` is a special custom quantity to be used with the +[`PostprocessCallback`](@ref). + +```@autodocs +Modules = [TrixiParticles] +Pages = ["general/mechanical_work_calculator.jl"] +``` diff --git a/src/TrixiParticles.jl b/src/TrixiParticles.jl index 7101be8e49..1104b3a5f8 100644 --- a/src/TrixiParticles.jl +++ b/src/TrixiParticles.jl @@ -74,8 +74,7 @@ export WeaklyCompressibleSPHSystem, EntropicallyDampedSPHSystem, TotalLagrangian export BoundaryZone, InFlow, OutFlow, BidirectionalFlow export InfoCallback, SolutionSavingCallback, DensityReinitializationCallback, PostprocessCallback, StepsizeCallback, UpdateCallback, SteadyStateReachedCallback, - SplitIntegrationCallback, MechanicalWorkCalculatorCallback, - calculated_mechanical_work, + SplitIntegrationCallback, MechanicalWorkCalculator, calculated_mechanical_work, SortingCallback export ContinuityDensity, SummationDensity export PenaltyForceGanzenmueller, TransportVelocityAdami, ParticleShiftingTechnique, diff --git a/src/callbacks/callbacks.jl b/src/callbacks/callbacks.jl index 3f295a6bc8..de8b09986a 100644 --- a/src/callbacks/callbacks.jl +++ b/src/callbacks/callbacks.jl @@ -76,5 +76,4 @@ include("stepsize.jl") include("update.jl") include("steady_state_reached.jl") include("split_integration.jl") -include("mechanical_work_calculator.jl") include("sorting.jl") diff --git a/src/general/general.jl b/src/general/general.jl index 3e9c868507..0615c8124f 100644 --- a/src/general/general.jl +++ b/src/general/general.jl @@ -8,4 +8,5 @@ include("initial_condition.jl") include("buffer.jl") include("interpolation.jl") include("custom_quantities.jl") +include("mechanical_work_calculator.jl") include("time_integration.jl") diff --git a/src/callbacks/mechanical_work_calculator.jl b/src/general/mechanical_work_calculator.jl similarity index 50% rename from src/callbacks/mechanical_work_calculator.jl rename to src/general/mechanical_work_calculator.jl index f20672af09..868562365a 100644 --- a/src/callbacks/mechanical_work_calculator.jl +++ b/src/general/mechanical_work_calculator.jl @@ -1,10 +1,12 @@ """ - MechanicalWorkCalculatorCallback(system::TotalLagrangianSPHSystem, semi; interval=1, - eachparticle=(n_integrated_particles(system) + 1):nparticles(system), - only_compute_force_on_fluid=false) + MechanicalWorkCalculator(system::TotalLagrangianSPHSystem, semi; + eachparticle=(n_integrated_particles(system) + 1):nparticles(system), + only_compute_force_on_fluid=false) -Callback that accumulates the work done by a set of particles in a +Functor that accumulates the work done by a set of particles in a [`TotalLagrangianSPHSystem`](@ref) by integrating the instantaneous power over time. +It can be passed as a custom quantity to [`PostprocessCallback`](@ref), which controls +when the work is sampled and whether it is written to a file. With the default arguments it tracks the work done by the clamped particles that follow a [`PrescribedMotion`](@ref). By selecting a different particle set, it can also @@ -17,7 +19,7 @@ be used to measure the work done by the structure on the surrounding fluid. `only_compute_force_on_fluid=true` to accumulate the work that the entire structure exerts on the surrounding fluid (useful for drag or lift estimates). -Internally the callback integrates the instantaneous power, i.e. the dot product between +Internally the calculator integrates the instantaneous power, i.e. the dot product between the force exerted by the particle and its prescribed velocity, using an explicit Euler time integration scheme. @@ -31,9 +33,6 @@ The accumulated value can be retrieved via [`calculated_mechanical_work`](@ref). - `semi`: The [`Semidiscretization`](@ref) that contains `system`. # Keywords -- `interval=1`: Interval (in number of time steps) at which to compute the instantaneous power. - It is recommended to keep this at `1` (every time step) or small (≤ 5) - to limit time integration errors in the integral. - `eachparticle=(n_integrated_particles(system) + 1):nparticles(system)`: Iterator selecting which particles contribute. The default includes all clamped particles in the system; pass `eachparticle(system)` to include every particle. @@ -55,21 +54,22 @@ ode = semidiscretize(semi, (0.0, 1.0)) semi_new = ode.p.semi system_new = semi_new.systems[1] -# Create a mechanical work calculator callback that is called every 2 time steps -mechanical_work_cb = MechanicalWorkCalculatorCallback(system_new, semi_new; interval=2) +# Create a mechanical work calculator that is called every 2 time steps. +mechanical_work_calculator = MechanicalWorkCalculator(system_new, semi_new) +postprocess_cb = PostprocessCallback(; interval=2, mechanical_work_calculator) -# After the simulation, retrieve the accumulated mechanical work -mechanical_work = calculated_mechanical_work(mechanical_work_cb) +# After the simulation, retrieve the accumulated mechanical work. +mechanical_work = calculated_mechanical_work(mechanical_work_calculator) # output [ Info: To create the self-interaction neighborhood search of a `TotalLagrangianSPHSystem`, a deep copy of the system is created inside the `Semidiscretization`. Use `system = semi.systems[i]` to access simulation data. 0.0 ``` """ -struct MechanicalWorkCalculatorCallback{T, DV, EP} - interval :: Int - t :: T # Time of last call - work :: T +mutable struct MechanicalWorkCalculator{ELTYPE, DV, EP} + t :: ELTYPE # Time of last call + work :: ELTYPE + initialized :: Bool system_index :: Int dv :: DV eachparticle :: EP @@ -78,102 +78,85 @@ end # This should dispatch on `TotalLagrangianSPHSystem`, but this name is not yet defined # due to the include order. -function MechanicalWorkCalculatorCallback(system::AbstractStructureSystem, semi; interval=1, - eachparticle=(n_integrated_particles(system) + 1):nparticles(system), - only_compute_force_on_fluid=false) +function MechanicalWorkCalculator(system::AbstractStructureSystem, semi; + eachparticle=(n_integrated_particles(system) + 1):nparticles(system), + only_compute_force_on_fluid=false) ELTYPE = eltype(system) system_index = system_indices(system, semi) - # Allocate buffer to write accelerations for all particles (including clamped ones) - dv = allocate(semi.parallelization_backend, ELTYPE, - (v_nvariables(system), nparticles(system))) - - # Note that time and work are initialized in - # `initialize_mechanical_work_calculator_callback`. - cb = MechanicalWorkCalculatorCallback(interval, Ref(zero(ELTYPE)), Ref(zero(ELTYPE)), - system_index, dv, eachparticle, - only_compute_force_on_fluid) + # Allocate buffer to write accelerations for all particles (including clamped ones). + # `PostprocessCallback` transfers data to the CPU before calling custom quantities, + # so this can be a regular `Array` even when the simulation is running on a GPU. + dv = Array{ELTYPE, 2}(undef, (v_nvariables(system), nparticles(system))) - # The first one is the `condition`, the second the `affect!` - return DiscreteCallback(cb, cb, save_positions=(false, false), - initialize=initialize_mechanical_work_calculator_callback) + return MechanicalWorkCalculator(zero(ELTYPE), zero(ELTYPE), false, + system_index, dv, eachparticle, + only_compute_force_on_fluid) end -function initialize_mechanical_work_calculator_callback(discrete_callback, u, t, integrator) - work_callback = discrete_callback.affect! +function reset!(calculator::MechanicalWorkCalculator) + calculator.t = zero(eltype(calculator.t)) + calculator.work = zero(eltype(calculator.work)) + calculator.initialized = false - # Reset time and mechanical work - work_callback.t[] = t - work_callback.work[] = zero(eltype(work_callback.work)) + return calculator end """ - calculated_mechanical_work(cb::DiscreteCallback{<:Any, <:MechanicalWorkCalculatorCallback}) + calculated_mechanical_work(calculator::MechanicalWorkCalculator) -Get the accumulated mechanical work from a [`MechanicalWorkCalculatorCallback`](@ref). +Get the accumulated mechanical work from a [`MechanicalWorkCalculator`](@ref). # Arguments -- `cb`: The `DiscreteCallback` returned by [`MechanicalWorkCalculatorCallback`](@ref). +- `calculator`: The [`MechanicalWorkCalculator`](@ref) functor. # Examples ```jldoctest; output = false, setup = :(system = TotalLagrangianSPHSystem(RectangularShape(0.1, (3, 4), (0.1, 0.0), density=1.0); smoothing_kernel=WendlandC2Kernel{2}(), smoothing_length=1.0, young_modulus=1.0, poisson_ratio=1.0); semi = (; systems=(system,), parallelization_backend=SerialBackend())) -# Create a mechanical work calculator callback -mechanical_work_cb = MechanicalWorkCalculatorCallback(system, semi) +# Create a mechanical work calculator +mechanical_work_calculator = MechanicalWorkCalculator(system, semi) # After the simulation, retrieve the accumulated mechanical work -mechanical_work = calculated_mechanical_work(mechanical_work_cb) +mechanical_work = calculated_mechanical_work(mechanical_work_calculator) # output 0.0 ``` """ -function calculated_mechanical_work(cb::DiscreteCallback{<:Any, - <:MechanicalWorkCalculatorCallback}) - return cb.affect!.work[] -end - -# `condition` -function (callback::MechanicalWorkCalculatorCallback)(u, t, integrator) - (; interval) = callback - - return condition_integrator_interval(integrator, interval) +function calculated_mechanical_work(calculator::MechanicalWorkCalculator) + return calculator.work end -# `affect!` -function (callback::MechanicalWorkCalculatorCallback)(integrator) - # Determine time step size as difference to last time this callback was called - t = integrator.t - dt = t - callback.t[] +function (calculator::MechanicalWorkCalculator)(system, dv_ode, du_ode, v_ode, u_ode, + semi, t) + if system_indices(system, semi) != calculator.system_index + return nothing + end - # Update time of last call - callback.t[] = t + if !calculator.initialized + calculator.t = t + calculator.work = zero(eltype(calculator.work)) + calculator.initialized = true + return calculator.work + end - semi = integrator.p.semi - v_ode, u_ode = integrator.u.x - work = callback.work + dt = t - calculator.t + calculator.t = t - system = semi.systems[callback.system_index] - update_mechanical_work_calculator!(work, system, callback.eachparticle, - callback.only_compute_force_on_fluid, callback.dv, - v_ode, u_ode, semi, t, dt) + work = update_mechanical_work!(calculator.work, system, calculator.eachparticle, + calculator.only_compute_force_on_fluid, calculator.dv, + v_ode, u_ode, semi, t, dt) - # This callback only processes results and does not change the result of the right-hand side. - derivative_discontinuity!(integrator, false) + calculator.work = work - return integrator + return calculator.work end -function update_mechanical_work_calculator!(work, system, eachparticle, - only_compute_force_on_fluid, dv, - v_ode, u_ode, semi, t, dt) +function update_mechanical_work!(work, system, eachparticle, + only_compute_force_on_fluid, dv, + v_ode, u_ode, semi, t, dt) + # Note that the systems and NHS have already been updated by the + # `PostprocessCallback` before calling this function. @trixi_timeit timer() "calculate mechanical work" begin - # Update quantities that are stored in the systems. These quantities (e.g. pressure) - # still have the values from the last stage of the previous step if not updated here. - @trixi_timeit timer() "update systems and nhs" begin - # Don't create sub-timers here to avoid cluttering the timer output - @notimeit timer() update_systems_and_nhs(v_ode, u_ode, semi, t) - end - set_zero!(dv) v = wrap_v(v_ode, system, semi) @@ -182,7 +165,7 @@ function update_mechanical_work_calculator!(work, system, eachparticle, foreach_system(semi) do neighbor_system if only_compute_force_on_fluid && !(neighbor_system isa AbstractFluidSystem) # Not a fluid system, ignore this system - return + return nothing end v_neighbor = wrap_v(v_ode, neighbor_system, semi) @@ -192,6 +175,8 @@ function update_mechanical_work_calculator!(work, system, eachparticle, system, neighbor_system, semi, integrate_tlsph=true, # Required when using split integration eachparticle=eachparticle) + + return nothing end if !only_compute_force_on_fluid @@ -201,6 +186,7 @@ function update_mechanical_work_calculator!(work, system, eachparticle, end end + # Note that this is a reduction, so we cannot use `@threaded` here. for particle in eachparticle velocity = current_velocity(v, system, particle) dv_particle = extract_svector(dv, system, particle) @@ -211,33 +197,9 @@ function update_mechanical_work_calculator!(work, system, eachparticle, # To obtain mechanical work, we need to integrate the instantaneous power. # Instantaneous power is force applied BY the particle times its velocity. # The force applied BY the particle is the negative of the force applied ON it. - work[] += dot(-F_particle, velocity) * dt + work += dot(-F_particle, velocity) * dt end end return work end - -function Base.show(io::IO, cb::DiscreteCallback{<:Any, <:MechanicalWorkCalculatorCallback}) - @nospecialize cb # reduce precompilation time - - ELTYPE = eltype(cb.affect!.work) - print(io, "MechanicalWorkCalculatorCallback{$ELTYPE}(interval=", cb.affect!.interval, - ")") -end - -function Base.show(io::IO, ::MIME"text/plain", - cb::DiscreteCallback{<:Any, <:MechanicalWorkCalculatorCallback}) - @nospecialize cb # reduce precompilation time - - if get(io, :compact, false) - show(io, cb) - else - update_cb = cb.affect! - ELTYPE = eltype(update_cb.work) - setup = [ - "interval" => update_cb.interval - ] - summary_box(io, "MechanicalWorkCalculatorCallback{$ELTYPE}", setup) - end -end diff --git a/test/callbacks/mechanical_work_calculator.jl b/test/callbacks/mechanical_work_calculator.jl index 31496fb45d..7bde46f5d3 100644 --- a/test/callbacks/mechanical_work_calculator.jl +++ b/test/callbacks/mechanical_work_calculator.jl @@ -1,4 +1,4 @@ -@testset verbose=true "MechanicalWorkCalculatorCallback" begin +@testset verbose=true "MechanicalWorkCalculator" begin # Mock system struct MockSystem <: TrixiParticles.AbstractStructureSystem{2} eltype::Type @@ -18,44 +18,29 @@ @testset "Constructor and Basic Properties" begin # Test default constructor - callback = MechanicalWorkCalculatorCallback(system64, semi64) - @test callback.affect!.system_index == 1 - @test callback.affect!.interval == 1 - @test callback.affect!.t[] == 0.0 - @test callback.affect!.work[] == 0.0 - @test callback.affect!.dv isa Array{Float64, 2} - @test size(callback.affect!.dv) == (2, 4) - @test callback.affect!.eachparticle == 5:4 - @test calculated_mechanical_work(callback) == 0.0 - - # Test constructor with interval - callback = MechanicalWorkCalculatorCallback(system64, semi64; interval=5) - @test callback.affect!.interval == 5 - @test eltype(callback.affect!.work) == Float64 - @test eltype(callback.affect!.t) == Float64 + calculator = MechanicalWorkCalculator(system64, semi64) + @test calculator.system_index == 1 + @test calculator.t == 0.0 + @test calculator.work == 0.0 + @test !calculator.initialized + @test calculator.dv isa Array{Float64, 2} + @test size(calculator.dv) == (2, 4) + @test calculator.eachparticle == 5:4 + @test calculated_mechanical_work(calculator) == 0.0 + + # Test constructor with explicit particle range + calculator = MechanicalWorkCalculator(system64, semi64; eachparticle=1:2) + @test calculator.eachparticle == 1:2 + @test eltype(calculator.work) == Float64 + @test eltype(calculator.t) == Float64 # Test with specific element type - callback = MechanicalWorkCalculatorCallback(system32, semi32; interval=2) - @test eltype(callback.affect!.work) == Float32 - @test eltype(callback.affect!.t) == Float32 + calculator = MechanicalWorkCalculator(system32, semi32) + @test eltype(calculator.work) == Float32 + @test eltype(calculator.t) == Float32 end - @testset "show" begin - callback = MechanicalWorkCalculatorCallback(system64, semi64; interval=10) - - # Test compact representation - show_compact = "MechanicalWorkCalculatorCallback{Float64}(interval=10)" - @test repr(callback) == show_compact - - # Test detailed representation - check against expected box format - show_box = """ - ┌──────────────────────────────────────────────────────────────────────────────────────────────────┐ - │ MechanicalWorkCalculatorCallback{Float64} │ - │ ═════════════════════════════════════════ │ - │ interval: ……………………………………………………… 10 │ - └──────────────────────────────────────────────────────────────────────────────────────────────────┘""" - @test repr("text/plain", callback) == show_box - end + @testset "update_mechanical_work_calculator!" begin # In the first test, we just move the 2x2 grid of particles up against gravity @@ -110,56 +95,53 @@ TrixiParticles.update_quantities!(system, v, u, v_ode, u_ode, semi, 0.0) # Set up test parameters - work1 = Ref(0.0) + work1 = 0.0 dt1 = 0.1 # Test that mechanical work is integrated, i.e., values of instantaneous # power are accumulated over time. This initial work should just be an offset. # Also, half the step size means half the work increase. - work2 = Ref(1.0) + work2 = 1.0 dt2 = 0.05 # Test `only_compute_force_on_fluid` - work3 = Ref(0.0) + work3 = 0.0 dt3 = 0.1 eachparticle = (TrixiParticles.n_integrated_particles(system) + 1):nparticles(system) dv = zeros(2, nparticles(system)) - TrixiParticles.update_mechanical_work_calculator!(work1, system, eachparticle, - false, - dv, v_ode, u_ode, semi, 0.0, - dt1) - TrixiParticles.update_mechanical_work_calculator!(work2, system, eachparticle, - false, - dv, v_ode, u_ode, semi, 0.0, - dt2) - TrixiParticles.update_mechanical_work_calculator!(work3, system, eachparticle, - true, - dv, v_ode, u_ode, semi, 0.0, - dt3) + work1 = TrixiParticles.update_mechanical_work!(work1, system, eachparticle, + false, dv, v_ode, u_ode, semi, + 0.0, dt1) + work2 = TrixiParticles.update_mechanical_work!(work2, system, eachparticle, + false, dv, v_ode, u_ode, semi, + 0.0, dt2) + work3 = TrixiParticles.update_mechanical_work!(work3, system, eachparticle, + true, dv, v_ode, u_ode, semi, + 0.0, dt3) if i == 1 - @test isapprox(work1[], 0.8) - @test isapprox(work2[], 1.0 + 0.4) + @test isapprox(work1, 0.8) + @test isapprox(work2, 1.0 + 0.4) elseif i == 2 # For very soft material, we can just pull up the top row of particles # and the work required is almost just the potential energy difference. - @test isapprox(work1[], 0.4080357142857143) - @test isapprox(work2[], 1.0 + 0.5 * 0.4080357142857143) + @test isapprox(work1, 0.4080357142857143) + @test isapprox(work2, 1.0 + 0.5 * 0.4080357142857143) elseif i == 3 # For a stiffer material, the stress from the offset creates larger forces # pulling the clamped particles back down, so we need a lot of work # to pull the material apart. - @test isapprox(work1[], 803.9714285714281) - @test isapprox(work2[], 1.0 + 0.5 * 803.9714285714281) + @test isapprox(work1, 803.9714285714281) + @test isapprox(work2, 1.0 + 0.5 * 803.9714285714281) elseif i == 4 # For a very stiff material, the work is even larger. - @test isapprox(work1[], 80357.5428571428) - @test isapprox(work2[], 1.0 + 0.5 * 80357.5428571428) + @test isapprox(work1, 80357.5428571428) + @test isapprox(work2, 1.0 + 0.5 * 80357.5428571428) end - @test isapprox(work3[], 0.0) + @test isapprox(work3, 0.0) end end end diff --git a/test/examples/examples.jl b/test/examples/examples.jl index 7498147374..5803652b68 100644 --- a/test/examples/examples.jl +++ b/test/examples/examples.jl @@ -91,7 +91,7 @@ end end - @trixi_testset "structure/oscillating_beam_2d.jl with MechanicalWorkCalculatorCallback" begin + @trixi_testset "structure/oscillating_beam_2d.jl with MechanicalWorkCalculator" begin # Load variables from the example trixi_include(@__MODULE__, joinpath(examples_dir(), "structure", "oscillating_beam_2d.jl"), @@ -129,17 +129,18 @@ ode = semidiscretize(semi, (0.0, 1.0)) system = ode.p.semi.systems[1] - mechanical_work_calculator = MechanicalWorkCalculatorCallback(system, semi; - interval=1) + mechanical_work = MechanicalWorkCalculator(system, semi) + postprocess_callback = PostprocessCallback(; interval=1, mechanical_work, + write_file_interval=0) sol = @trixi_test_nowarn solve(ode, RDPK3SpFSAL49(), save_everystep=false, - callback=mechanical_work_calculator) + callback=postprocess_callback) @test sol.retcode == ReturnCode.Success @test count_rhs_allocations(sol) == 0 # Potential energy difference should be m * g * h - @test isapprox(calculated_mechanical_work(mechanical_work_calculator), + @test isapprox(calculated_mechanical_work(mechanical_work), sum(system.mass) * gravity * 1, rtol=rtol[name]) end @@ -156,9 +157,9 @@ end @testset verbose=true "FSI" begin - @trixi_testset "fluid/hydrostatic_water_column_2d.jl with MechanicalWorkCalculatorCallback and moving TLSPH walls" begin + @trixi_testset "fluid/hydrostatic_water_column_2d.jl with MechanicalWorkCalculator and moving TLSPH walls" begin # In this test, we move a water-filled tank up against gravity by 1 unit - # and verify that the work accumulated by the `MechanicalWorkCalculatorCallback` + # and verify that the work accumulated by the `MechanicalWorkCalculator` # matches the expected potential energy difference. # Load variables from the example @@ -181,7 +182,7 @@ # Create TLSPH system for the tank walls and clamp all particles. # This is identical to a `WallBoundarySystem`, but now we can - # use the `MechanicalWorkCalculatorCallback` to compute the mechanical work. + # use the `MechanicalWorkCalculator` to compute the mechanical work. boundary_spacing = tank.boundary.particle_spacing tlsph_kernel = WendlandC2Kernel{2}() tlsph_smoothing_length = sqrt(2) * boundary_spacing @@ -203,18 +204,16 @@ tlsph_system_new = ode.p.semi.systems[2] # Mechanical work calculators for fluid + tank and fluid only - mechanical_work_calculator1 = MechanicalWorkCalculatorCallback(tlsph_system_new, - semi; - interval=1) - mechanical_work_calculator2 = MechanicalWorkCalculatorCallback(tlsph_system_new, - semi; - interval=1, - only_compute_force_on_fluid=true) + mechanical_work1 = MechanicalWorkCalculator(tlsph_system_new, semi) + mechanical_work2 = MechanicalWorkCalculator(tlsph_system_new, semi; + only_compute_force_on_fluid=true) + postprocess_callback = PostprocessCallback(; interval=1, mechanical_work1, + mechanical_work2, + write_file_interval=0) sol = @trixi_test_nowarn solve(ode, RDPK3SpFSAL35(), save_everystep=false, callback=CallbackSet(info_callback, - mechanical_work_calculator1, - mechanical_work_calculator2)) + postprocess_callback)) @test sol.retcode == ReturnCode.Success @test count_rhs_allocations(sol) == 0 @@ -229,9 +228,9 @@ # compressible and is deformed during the simulation. # A slower prescribed motion (e.g., over 2 seconds instead of 1) or a higher # speed of sound in the fluid would improve accuracy (and increase runtime). - @test isapprox(calculated_mechanical_work(mechanical_work_calculator1), + @test isapprox(calculated_mechanical_work(mechanical_work1), expected_energy_fluid + expected_energy_tank, rtol=5e-4) - @test isapprox(calculated_mechanical_work(mechanical_work_calculator2), + @test isapprox(calculated_mechanical_work(mechanical_work2), expected_energy_fluid, rtol=5e-4) end From 874ee6cb49be8ac9ceebdf49722d7418d31ebf4d Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Mon, 1 Jun 2026 11:38:03 +0200 Subject: [PATCH 075/134] Fix --- src/general/mechanical_work_calculator.jl | 6 +++--- test/callbacks/mechanical_work_calculator.jl | 2 -- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/general/mechanical_work_calculator.jl b/src/general/mechanical_work_calculator.jl index 868562365a..e0e96e0581 100644 --- a/src/general/mechanical_work_calculator.jl +++ b/src/general/mechanical_work_calculator.jl @@ -95,8 +95,8 @@ function MechanicalWorkCalculator(system::AbstractStructureSystem, semi; end function reset!(calculator::MechanicalWorkCalculator) - calculator.t = zero(eltype(calculator.t)) - calculator.work = zero(eltype(calculator.work)) + calculator.t = zero(calculator.t) + calculator.work = zero(calculator.work) calculator.initialized = false return calculator @@ -134,7 +134,7 @@ function (calculator::MechanicalWorkCalculator)(system, dv_ode, du_ode, v_ode, u if !calculator.initialized calculator.t = t - calculator.work = zero(eltype(calculator.work)) + calculator.work = zero(calculator.work) calculator.initialized = true return calculator.work end diff --git a/test/callbacks/mechanical_work_calculator.jl b/test/callbacks/mechanical_work_calculator.jl index 7bde46f5d3..cdcb8cfc56 100644 --- a/test/callbacks/mechanical_work_calculator.jl +++ b/test/callbacks/mechanical_work_calculator.jl @@ -40,8 +40,6 @@ @test eltype(calculator.t) == Float32 end - - @testset "update_mechanical_work_calculator!" begin # In the first test, we just move the 2x2 grid of particles up against gravity # and test that the accumulated work is just the potential energy difference. From c9463a341b6f9b2faa63fbaf2c7dadf519e84d2f Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Mon, 1 Jun 2026 13:14:13 +0200 Subject: [PATCH 076/134] Update docstrings --- src/general/mechanical_work_calculator.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/general/mechanical_work_calculator.jl b/src/general/mechanical_work_calculator.jl index e0e96e0581..2f6b6ac53d 100644 --- a/src/general/mechanical_work_calculator.jl +++ b/src/general/mechanical_work_calculator.jl @@ -15,9 +15,10 @@ be used to measure the work done by the structure on the surrounding fluid. - **Prescribed/clamped motion work** (default) -- monitor only the clamped particles by leaving `eachparticle` at its default range `(n_integrated_particles(system) + 1):nparticles(system)`. -- **Fluid load measurement** -- set `eachparticle=eachparticle(system)` together with +- **Fluid energy transfer** -- set `eachparticle=eachparticle(system)` together with `only_compute_force_on_fluid=true` to accumulate the work that the entire structure - exerts on the surrounding fluid (useful for drag or lift estimates). + exerts on the surrounding fluid. This measures energy transfer, not force; use + [`ThrustCalculator`](@ref) for drag, lift, or thrust estimates. Internally the calculator integrates the instantaneous power, i.e. the dot product between the force exerted by the particle and its prescribed velocity, using an explicit Euler @@ -39,8 +40,7 @@ The accumulated value can be retrieved via [`calculated_mechanical_work`](@ref). - `only_compute_force_on_fluid=false`: When `true`, only interactions with fluid systems are accounted for. Combined with `eachparticle=eachparticle(system)`, this accumulates the work that the - entire structure exerts on the fluid, which is useful for drag or lift - estimates. + entire structure exerts on the fluid. # Examples ```jldoctest; output = false, setup = :(system = TotalLagrangianSPHSystem(RectangularShape(0.1, (3, 4), (0.1, 0.0), density=1.0); smoothing_kernel=WendlandC2Kernel{2}(), smoothing_length=1.0, young_modulus=1.0, poisson_ratio=1.0); semi = (; systems=(system,), parallelization_backend=SerialBackend())) From ae92e89ecf4fb2db01362e997c14cccddef489e1 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Mon, 1 Jun 2026 14:30:36 +0200 Subject: [PATCH 077/134] Extract function --- src/general/mechanical_work_calculator.jl | 66 +++++++++++++---------- 1 file changed, 38 insertions(+), 28 deletions(-) diff --git a/src/general/mechanical_work_calculator.jl b/src/general/mechanical_work_calculator.jl index 2f6b6ac53d..fb12562b4e 100644 --- a/src/general/mechanical_work_calculator.jl +++ b/src/general/mechanical_work_calculator.jl @@ -157,34 +157,9 @@ function update_mechanical_work!(work, system, eachparticle, # Note that the systems and NHS have already been updated by the # `PostprocessCallback` before calling this function. @trixi_timeit timer() "calculate mechanical work" begin - set_zero!(dv) - - v = wrap_v(v_ode, system, semi) - u = wrap_u(u_ode, system, semi) - - foreach_system(semi) do neighbor_system - if only_compute_force_on_fluid && !(neighbor_system isa AbstractFluidSystem) - # Not a fluid system, ignore this system - return nothing - end - - v_neighbor = wrap_v(v_ode, neighbor_system, semi) - u_neighbor = wrap_u(u_ode, neighbor_system, semi) - - interact!(dv, v, u, v_neighbor, u_neighbor, - system, neighbor_system, semi, - integrate_tlsph=true, # Required when using split integration - eachparticle=eachparticle) - - return nothing - end - - if !only_compute_force_on_fluid - @threaded semi for particle in eachparticle - add_acceleration!(dv, system, particle) - add_source_terms_inner!(dv, v, u, particle, system, source_terms(system), t) - end - end + v = compute_structure_acceleration!(dv, system, eachparticle, + only_compute_force_on_fluid, + v_ode, u_ode, semi, t) # Note that this is a reduction, so we cannot use `@threaded` here. for particle in eachparticle @@ -203,3 +178,38 @@ function update_mechanical_work!(work, system, eachparticle, return work end + +function compute_structure_acceleration!(dv, system, eachparticle, + only_compute_force_on_fluid, + v_ode, u_ode, semi, t) + set_zero!(dv) + + v = wrap_v(v_ode, system, semi) + u = wrap_u(u_ode, system, semi) + + foreach_system(semi) do neighbor_system + if only_compute_force_on_fluid && !(neighbor_system isa AbstractFluidSystem) + # Not a fluid system, ignore this system. + return nothing + end + + v_neighbor = wrap_v(v_ode, neighbor_system, semi) + u_neighbor = wrap_u(u_ode, neighbor_system, semi) + + interact!(dv, v, u, v_neighbor, u_neighbor, + system, neighbor_system, semi, + integrate_tlsph=true, # Required when using split integration + eachparticle=eachparticle) + + return nothing + end + + if !only_compute_force_on_fluid + @threaded semi for particle in eachparticle + add_acceleration!(dv, system, particle) + add_source_terms_inner!(dv, v, u, particle, system, source_terms(system), t) + end + end + + return v +end From 2606366ec8e0716835da7f4f5fdbcecef51f5a8a Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Mon, 1 Jun 2026 15:01:36 +0200 Subject: [PATCH 078/134] Fix docstring --- src/general/mechanical_work_calculator.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/general/mechanical_work_calculator.jl b/src/general/mechanical_work_calculator.jl index fb12562b4e..e60e2885f0 100644 --- a/src/general/mechanical_work_calculator.jl +++ b/src/general/mechanical_work_calculator.jl @@ -17,8 +17,7 @@ be used to measure the work done by the structure on the surrounding fluid. `(n_integrated_particles(system) + 1):nparticles(system)`. - **Fluid energy transfer** -- set `eachparticle=eachparticle(system)` together with `only_compute_force_on_fluid=true` to accumulate the work that the entire structure - exerts on the surrounding fluid. This measures energy transfer, not force; use - [`ThrustCalculator`](@ref) for drag, lift, or thrust estimates. + exerts on the surrounding fluid. Internally the calculator integrates the instantaneous power, i.e. the dot product between the force exerted by the particle and its prescribed velocity, using an explicit Euler From 2ccdb381785e8420fee7d7423fc80a0f5de1b392 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Mon, 1 Jun 2026 15:42:23 +0200 Subject: [PATCH 079/134] Fix return value --- src/general/mechanical_work_calculator.jl | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/general/mechanical_work_calculator.jl b/src/general/mechanical_work_calculator.jl index e60e2885f0..10cf322de6 100644 --- a/src/general/mechanical_work_calculator.jl +++ b/src/general/mechanical_work_calculator.jl @@ -156,9 +156,12 @@ function update_mechanical_work!(work, system, eachparticle, # Note that the systems and NHS have already been updated by the # `PostprocessCallback` before calling this function. @trixi_timeit timer() "calculate mechanical work" begin - v = compute_structure_acceleration!(dv, system, eachparticle, - only_compute_force_on_fluid, - v_ode, u_ode, semi, t) + v = wrap_v(v_ode, system, semi) + u = wrap_u(u_ode, system, semi) + + compute_structure_acceleration!(dv, v, u, system, eachparticle, + only_compute_force_on_fluid, + v_ode, u_ode, semi, t) # Note that this is a reduction, so we cannot use `@threaded` here. for particle in eachparticle @@ -178,14 +181,11 @@ function update_mechanical_work!(work, system, eachparticle, return work end -function compute_structure_acceleration!(dv, system, eachparticle, +function compute_structure_acceleration!(dv, v, u, system, eachparticle, only_compute_force_on_fluid, v_ode, u_ode, semi, t) set_zero!(dv) - v = wrap_v(v_ode, system, semi) - u = wrap_u(u_ode, system, semi) - foreach_system(semi) do neighbor_system if only_compute_force_on_fluid && !(neighbor_system isa AbstractFluidSystem) # Not a fluid system, ignore this system. @@ -210,5 +210,5 @@ function compute_structure_acceleration!(dv, system, eachparticle, end end - return v + return dv end From 62e57555226d26acaf89be8eccb884e7e3ddbeca Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Mon, 1 Jun 2026 15:44:30 +0200 Subject: [PATCH 080/134] Fix return values --- src/general/mechanical_work_calculator.jl | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/general/mechanical_work_calculator.jl b/src/general/mechanical_work_calculator.jl index 10cf322de6..b7cd31775b 100644 --- a/src/general/mechanical_work_calculator.jl +++ b/src/general/mechanical_work_calculator.jl @@ -141,18 +141,18 @@ function (calculator::MechanicalWorkCalculator)(system, dv_ode, du_ode, v_ode, u dt = t - calculator.t calculator.t = t - work = update_mechanical_work!(calculator.work, system, calculator.eachparticle, - calculator.only_compute_force_on_fluid, calculator.dv, - v_ode, u_ode, semi, t, dt) + work = update_mechanical_work(calculator.work, system, calculator.eachparticle, + calculator.only_compute_force_on_fluid, calculator.dv, + v_ode, u_ode, semi, t, dt) calculator.work = work return calculator.work end -function update_mechanical_work!(work, system, eachparticle, - only_compute_force_on_fluid, dv, - v_ode, u_ode, semi, t, dt) +function update_mechanical_work(work, system, eachparticle, + only_compute_force_on_fluid, dv, + v_ode, u_ode, semi, t, dt) # Note that the systems and NHS have already been updated by the # `PostprocessCallback` before calling this function. @trixi_timeit timer() "calculate mechanical work" begin @@ -178,6 +178,8 @@ function update_mechanical_work!(work, system, eachparticle, end end + # Note that we cannot `return` from inside the `@trixi_timeit` block, + # or the timer output formatting will be messed up. return work end From 2a0d226c7c9056ab52bce71619fcf13caeb3b206 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Mon, 1 Jun 2026 15:46:31 +0200 Subject: [PATCH 081/134] Move timer out of inner function --- src/general/mechanical_work_calculator.jl | 48 +++++++++++------------ 1 file changed, 22 insertions(+), 26 deletions(-) diff --git a/src/general/mechanical_work_calculator.jl b/src/general/mechanical_work_calculator.jl index b7cd31775b..eb59689f61 100644 --- a/src/general/mechanical_work_calculator.jl +++ b/src/general/mechanical_work_calculator.jl @@ -141,11 +141,13 @@ function (calculator::MechanicalWorkCalculator)(system, dv_ode, du_ode, v_ode, u dt = t - calculator.t calculator.t = t - work = update_mechanical_work(calculator.work, system, calculator.eachparticle, - calculator.only_compute_force_on_fluid, calculator.dv, - v_ode, u_ode, semi, t, dt) - - calculator.work = work + @trixi_timeit timer() "calculate mechanical work" begin + calculator.work = update_mechanical_work(calculator.work, system, + calculator.eachparticle, + calculator.only_compute_force_on_fluid, + calculator.dv, + v_ode, u_ode, semi, t, dt) + end return calculator.work end @@ -153,33 +155,27 @@ end function update_mechanical_work(work, system, eachparticle, only_compute_force_on_fluid, dv, v_ode, u_ode, semi, t, dt) - # Note that the systems and NHS have already been updated by the - # `PostprocessCallback` before calling this function. - @trixi_timeit timer() "calculate mechanical work" begin - v = wrap_v(v_ode, system, semi) - u = wrap_u(u_ode, system, semi) + v = wrap_v(v_ode, system, semi) + u = wrap_u(u_ode, system, semi) - compute_structure_acceleration!(dv, v, u, system, eachparticle, - only_compute_force_on_fluid, - v_ode, u_ode, semi, t) + compute_structure_acceleration!(dv, v, u, system, eachparticle, + only_compute_force_on_fluid, + v_ode, u_ode, semi, t) - # Note that this is a reduction, so we cannot use `@threaded` here. - for particle in eachparticle - velocity = current_velocity(v, system, particle) - dv_particle = extract_svector(dv, system, particle) + # Note that this is a reduction, so we cannot use `@threaded` here. + for particle in eachparticle + velocity = current_velocity(v, system, particle) + dv_particle = extract_svector(dv, system, particle) - # The force on the clamped particle is mass times acceleration - F_particle = system.mass[particle] * dv_particle + # The force on the clamped particle is mass times acceleration + F_particle = system.mass[particle] * dv_particle - # To obtain mechanical work, we need to integrate the instantaneous power. - # Instantaneous power is force applied BY the particle times its velocity. - # The force applied BY the particle is the negative of the force applied ON it. - work += dot(-F_particle, velocity) * dt - end + # To obtain mechanical work, we need to integrate the instantaneous power. + # Instantaneous power is force applied BY the particle times its velocity. + # The force applied BY the particle is the negative of the force applied ON it. + work += dot(-F_particle, velocity) * dt end - # Note that we cannot `return` from inside the `@trixi_timeit` block, - # or the timer output formatting will be messed up. return work end From 057661e74264582fa8f21aaeff391d419a91d456 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Mon, 1 Jun 2026 14:31:22 +0200 Subject: [PATCH 082/134] Add `ThrustCalculator` --- src/TrixiParticles.jl | 1 + src/general/mechanical_work_calculator.jl | 124 +++++++++++++++++++ test/callbacks/mechanical_work_calculator.jl | 100 ++++++++++++++- test/examples/examples.jl | 4 +- 4 files changed, 227 insertions(+), 2 deletions(-) diff --git a/src/TrixiParticles.jl b/src/TrixiParticles.jl index 1104b3a5f8..ace8012fc2 100644 --- a/src/TrixiParticles.jl +++ b/src/TrixiParticles.jl @@ -75,6 +75,7 @@ export BoundaryZone, InFlow, OutFlow, BidirectionalFlow export InfoCallback, SolutionSavingCallback, DensityReinitializationCallback, PostprocessCallback, StepsizeCallback, UpdateCallback, SteadyStateReachedCallback, SplitIntegrationCallback, MechanicalWorkCalculator, calculated_mechanical_work, + ThrustCalculator, calculated_thrust, SortingCallback export ContinuityDensity, SummationDensity export PenaltyForceGanzenmueller, TransportVelocityAdami, ParticleShiftingTechnique, diff --git a/src/general/mechanical_work_calculator.jl b/src/general/mechanical_work_calculator.jl index eb59689f61..f03f7b0b11 100644 --- a/src/general/mechanical_work_calculator.jl +++ b/src/general/mechanical_work_calculator.jl @@ -210,3 +210,127 @@ function compute_structure_acceleration!(dv, v, u, system, eachparticle, return dv end + +""" + ThrustCalculator(system::TotalLagrangianSPHSystem, semi; + direction, + eachparticle=eachparticle(system)) + +Functor that computes the instantaneous hydrodynamic force exerted by the fluid on a +[`TotalLagrangianSPHSystem`](@ref), projected onto `direction`. +It can be passed as a custom quantity to [`PostprocessCallback`](@ref). + +For a fixed fluid-interacting structure in a channel flow, choose `direction` as the +direction of useful force and multiply the recorded thrust by the corresponding reference +speed to obtain useful mechanical power. + +!!! warning "Experimental implementation" + This is an experimental feature and may change in future releases. + +# Arguments +- `system`: The [`TotalLagrangianSPHSystem`](@ref) whose hydrodynamic force should be + monitored. +- `semi`: The [`Semidiscretization`](@ref) that contains `system`. + +# Keywords +- `direction`: Direction onto which the hydrodynamic force is projected. The vector is + normalized internally. +- `eachparticle=eachparticle(system)`: Iterator selecting which particles contribute. + +# Examples +```jldoctest; output = false, setup = :(system = TotalLagrangianSPHSystem(RectangularShape(0.1, (3, 4), (0.1, 0.0), density=1.0); smoothing_kernel=WendlandC2Kernel{2}(), smoothing_length=1.0, young_modulus=1.0, poisson_ratio=1.0); semi = (; systems=(system,), parallelization_backend=SerialBackend())) +# Create a thrust calculator in x-direction +thrust_calculator = ThrustCalculator(system, semi; direction=SVector(1.0, 0.0)) + +# After postprocessing, retrieve the latest thrust value +thrust = calculated_thrust(thrust_calculator) + +# output +0.0 +``` +""" +mutable struct ThrustCalculator{ELTYPE, DV, EP, D} + thrust :: ELTYPE + system_index :: Int + dv :: DV + eachparticle :: EP + direction :: D +end + +# This should dispatch on `TotalLagrangianSPHSystem`, but this name is not yet defined +# due to the include order. +function ThrustCalculator(system::AbstractStructureSystem, semi; + direction, eachparticle=eachparticle(system)) + ELTYPE = eltype(system) + system_index = system_indices(system, semi) + + # Check vector length before converting to `SVector` to avoid extremely long + # compile times when accidentally passing large vectors. + if length(direction) != ndims(system) + throw(ArgumentError("length of `direction` must match the number of dimensions")) + end + direction_ = SVector(Tuple(direction)) + if iszero(direction_) + throw(ArgumentError("`direction` must not be zero")) + end + direction_ = normalize(direction_) + + # Allocate buffer to write hydrodynamic accelerations for all particles. + # `PostprocessCallback` transfers data to the CPU before calling custom quantities, + # so this can be a regular `Array` even when the simulation is running on a GPU. + dv = Array{ELTYPE, 2}(undef, (v_nvariables(system), nparticles(system))) + + return ThrustCalculator(zero(ELTYPE), system_index, dv, eachparticle, direction_) +end + +function reset!(calculator::ThrustCalculator) + calculator.thrust = zero(calculator.thrust) + + return calculator +end + +""" + calculated_thrust(calculator::ThrustCalculator) + +Get the latest projected hydrodynamic force from a [`ThrustCalculator`](@ref). +""" +function calculated_thrust(calculator::ThrustCalculator) + return calculator.thrust +end + +function (calculator::ThrustCalculator)(system, dv_ode, du_ode, v_ode, u_ode, semi, t) + if system_indices(system, semi) != calculator.system_index + return nothing + end + + thrust = update_thrust!(system, calculator.eachparticle, calculator.direction, + calculator.dv, v_ode, u_ode, semi, t) + + calculator.thrust = thrust + + return calculator.thrust +end + +function update_thrust!(system, eachparticle, direction, dv, v_ode, u_ode, semi, t) + # Note that the systems and NHS have already been updated by the + # `PostprocessCallback` before calling this function. + @trixi_timeit timer() "calculate thrust" begin + compute_structure_acceleration!(dv, system, eachparticle, true, + v_ode, u_ode, semi, t) + + return projected_force(dv, system, eachparticle, direction) + end +end + +function projected_force(dv, system, eachparticle, direction) + force = zero(eltype(system)) + + # Note that this is a reduction, so we cannot use `@threaded` here. + for particle in eachparticle + dv_particle = extract_svector(dv, system, particle) + force_particle = system.mass[particle] * dv_particle + force += dot(force_particle, direction) + end + + return force +end diff --git a/test/callbacks/mechanical_work_calculator.jl b/test/callbacks/mechanical_work_calculator.jl index cdcb8cfc56..badc3dfa46 100644 --- a/test/callbacks/mechanical_work_calculator.jl +++ b/test/callbacks/mechanical_work_calculator.jl @@ -2,8 +2,12 @@ # Mock system struct MockSystem <: TrixiParticles.AbstractStructureSystem{2} eltype::Type + mass::Vector + + function MockSystem(ELTYPE) + new(ELTYPE, ELTYPE[1, 2, 3, 4]) + end end - TrixiParticles.nparticles(::MockSystem) = 4 Base.eltype(system::MockSystem) = system.eltype function TrixiParticles.create_neighborhood_search(neighborhood_search, @@ -40,6 +44,100 @@ @test eltype(calculator.t) == Float32 end + @testset "ThrustCalculator constructor and force projection" begin + @test_throws UndefKeywordError ThrustCalculator(system64, semi64) + + calculator = ThrustCalculator(system64, semi64; direction=SVector(1.0, 0.0)) + @test calculator.system_index == 1 + @test calculator.thrust == 0.0 + @test calculator.dv isa Array{Float64, 2} + @test size(calculator.dv) == (2, 4) + @test calculator.eachparticle == eachparticle(system64) + @test calculator.direction == SVector(1.0, 0.0) + @test calculated_thrust(calculator) == 0.0 + + calculator = ThrustCalculator(system32, semi32; direction=(0.0, 2.0), + eachparticle=2:3) + @test eltype(calculator.thrust) == Float32 + @test calculator.direction == SVector(0.0f0, 1.0f0) + @test calculator.eachparticle == 2:3 + + @test_throws ArgumentError ThrustCalculator(system64, semi64; direction=(0.0, 0.0)) + + dv = [2.0 -1.0 0.0 3.0 + 4.0 5.0 -2.0 1.0] + @test TrixiParticles.projected_force(dv, system64, eachparticle(system64), + SVector(1.0, 0.0)) == 12.0 + @test TrixiParticles.projected_force(dv, system64, 2:3, + SVector(0.0, 1.0)) == 4.0 + + TrixiParticles.reset!(calculator) + @test calculated_thrust(calculator) == 0.0f0 + end + + @testset "ThrustCalculator FSI force" begin + particle_spacing = 1.0 + smoothing_kernel = SchoenbergCubicSplineKernel{2}() + smoothing_length = 1.0 + fluid_density = 1000.0 + structure_density = 2000.0 + particle_volume = particle_spacing^2 + + state_equation = StateEquationCole(sound_speed=10.0, + reference_density=fluid_density, + exponent=1.0) + + fluid_ic = InitialCondition(; coordinates=reshape([0.0, 0.0], 2, 1), + velocity=zeros(2, 1), + mass=[particle_volume * fluid_density], + density=[fluid_density], particle_spacing) + + fluid_system = WeaklyCompressibleSPHSystem(fluid_ic; smoothing_kernel, + smoothing_length, + density_calculator=SummationDensity(), + state_equation, + reference_particle_spacing=particle_spacing) + + structure_coordinates = reshape([1.5, 0.0], 2, 1) + structure_ic = InitialCondition(; coordinates=structure_coordinates, + velocity=zeros(2, 1), + mass=[particle_volume * structure_density], + density=[structure_density], particle_spacing) + + boundary_model = BoundaryModelDummyParticles([fluid_density], + [particle_volume * fluid_density], + AdamiPressureExtrapolation(), + smoothing_kernel, smoothing_length; + state_equation, + reference_particle_spacing=particle_spacing) + + structure_system = TotalLagrangianSPHSystem(structure_ic; smoothing_kernel, + smoothing_length, + young_modulus=1.0e5, + poisson_ratio=0.3, + boundary_model) + + semi_ = Semidiscretization(fluid_system, structure_system) + ode = semidiscretize(semi_, (0.0, 0.01)) + semi = ode.p.semi + + v_ode, u_ode = ode.u0.x + dv_ode = zero(v_ode) + TrixiParticles.kick!(dv_ode, v_ode, u_ode, ode.p, 0.0) + + fluid = semi.systems[1] + structure = semi.systems[2] + dv_fluid = TrixiParticles.wrap_v(dv_ode, fluid, semi) + + thrust = ThrustCalculator(structure, semi; direction=SVector(1.0, 0.0)) + thrust(structure, dv_ode, nothing, v_ode, u_ode, semi, 0.0) + + expected_force = -fluid.mass[1] * dv_fluid[1, 1] + @test !iszero(expected_force) + @test isapprox(calculated_thrust(thrust), expected_force; + rtol=sqrt(eps()), atol=sqrt(eps())) + end + @testset "update_mechanical_work_calculator!" begin # In the first test, we just move the 2x2 grid of particles up against gravity # and test that the accumulated work is just the potential energy difference. diff --git a/test/examples/examples.jl b/test/examples/examples.jl index 5803652b68..a98af097ab 100644 --- a/test/examples/examples.jl +++ b/test/examples/examples.jl @@ -207,8 +207,9 @@ mechanical_work1 = MechanicalWorkCalculator(tlsph_system_new, semi) mechanical_work2 = MechanicalWorkCalculator(tlsph_system_new, semi; only_compute_force_on_fluid=true) + thrust = ThrustCalculator(tlsph_system_new, semi; direction=SVector(0.0, 1.0)) postprocess_callback = PostprocessCallback(; interval=1, mechanical_work1, - mechanical_work2, + mechanical_work2, thrust, write_file_interval=0) sol = @trixi_test_nowarn solve(ode, RDPK3SpFSAL35(), save_everystep=false, @@ -233,6 +234,7 @@ @test isapprox(calculated_mechanical_work(mechanical_work2), expected_energy_fluid, rtol=5e-4) + @test isfinite(calculated_thrust(thrust)) end @trixi_testset "fsi/falling_water_column_2d.jl" begin From ca661228a47e007df3fb1ad133e052ef25778cf7 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Mon, 1 Jun 2026 15:47:35 +0200 Subject: [PATCH 083/134] Fix timer --- src/general/mechanical_work_calculator.jl | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/general/mechanical_work_calculator.jl b/src/general/mechanical_work_calculator.jl index f03f7b0b11..5206d15a09 100644 --- a/src/general/mechanical_work_calculator.jl +++ b/src/general/mechanical_work_calculator.jl @@ -303,23 +303,19 @@ function (calculator::ThrustCalculator)(system, dv_ode, du_ode, v_ode, u_ode, se return nothing end - thrust = update_thrust!(system, calculator.eachparticle, calculator.direction, - calculator.dv, v_ode, u_ode, semi, t) - - calculator.thrust = thrust + @trixi_timeit timer() "calculate thrust" begin + calculator.thrust = update_thrust!(system, calculator.eachparticle, + calculator.direction, + calculator.dv, v_ode, u_ode, semi, t) + end return calculator.thrust end function update_thrust!(system, eachparticle, direction, dv, v_ode, u_ode, semi, t) - # Note that the systems and NHS have already been updated by the - # `PostprocessCallback` before calling this function. - @trixi_timeit timer() "calculate thrust" begin - compute_structure_acceleration!(dv, system, eachparticle, true, - v_ode, u_ode, semi, t) + compute_structure_acceleration!(dv, system, eachparticle, true, v_ode, u_ode, semi, t) - return projected_force(dv, system, eachparticle, direction) - end + return projected_force(dv, system, eachparticle, direction) end function projected_force(dv, system, eachparticle, direction) From 5f4bb26609f220509970c9b0164f89be712d5a04 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Mon, 1 Jun 2026 15:53:06 +0200 Subject: [PATCH 084/134] Fix --- src/general/mechanical_work_calculator.jl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/general/mechanical_work_calculator.jl b/src/general/mechanical_work_calculator.jl index 5206d15a09..8dd78a62ec 100644 --- a/src/general/mechanical_work_calculator.jl +++ b/src/general/mechanical_work_calculator.jl @@ -304,7 +304,7 @@ function (calculator::ThrustCalculator)(system, dv_ode, du_ode, v_ode, u_ode, se end @trixi_timeit timer() "calculate thrust" begin - calculator.thrust = update_thrust!(system, calculator.eachparticle, + calculator.thrust = compute_thrust(system, calculator.eachparticle, calculator.direction, calculator.dv, v_ode, u_ode, semi, t) end @@ -312,8 +312,11 @@ function (calculator::ThrustCalculator)(system, dv_ode, du_ode, v_ode, u_ode, se return calculator.thrust end -function update_thrust!(system, eachparticle, direction, dv, v_ode, u_ode, semi, t) - compute_structure_acceleration!(dv, system, eachparticle, true, v_ode, u_ode, semi, t) +function compute_thrust(system, eachparticle, direction, dv, v_ode, u_ode, semi, t) + v = wrap_v(v_ode, system, semi) + u = wrap_u(u_ode, system, semi) + compute_structure_acceleration!(dv, v, u, system, eachparticle, true, + v_ode, u_ode, semi, t) return projected_force(dv, system, eachparticle, direction) end From 5c9942c7667f84f14f7a0abc565029c456f61772 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Wed, 2 Oct 2024 09:20:28 +0200 Subject: [PATCH 085/134] Add first fin simulation --- examples/fsi/fin_2d.jl | 164 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 examples/fsi/fin_2d.jl diff --git a/examples/fsi/fin_2d.jl b/examples/fsi/fin_2d.jl new file mode 100644 index 0000000000..41f7d7098f --- /dev/null +++ b/examples/fsi/fin_2d.jl @@ -0,0 +1,164 @@ +using TrixiParticles +using OrdinaryDiffEq + +# ========================================================================================== +# ==== Resolution +n_particles_y = 3 + +# ========================================================================================== +# ==== Experiment Setup +gravity = 2.0 +tspan = (0.0, 5.0) + +elastic_beam = (length=0.45, thickness=0.02) +material = (density=1000.0, E=1.0e7, nu=0.4) +clamp_radius = 0.05 + +# The structure starts at the position of the first particle and ends +# at the position of the last particle. +particle_spacing = elastic_beam.thickness / (n_particles_y - 1) + +# Add particle_spacing/2 to the clamp_radius to ensure that particles are also placed on the radius +fixed_particles = SphereShape(particle_spacing, clamp_radius + particle_spacing / 2, + (0.0, elastic_beam.thickness / 2), material.density, + cutout_min=(0.0, 0.0), + cutout_max=(clamp_radius, elastic_beam.thickness), + tlsph=true) + +n_particles_clamp_x = round(Int, clamp_radius / particle_spacing) + +# Beam and clamped particles +n_particles_per_dimension = (round(Int, elastic_beam.length / particle_spacing) + + n_particles_clamp_x + 1, n_particles_y) + +# Note that the `RectangularShape` puts the first particle half a particle spacing away +# from the boundary, which is correct for fluids, but not for solids. +# We therefore need to pass `tlsph=true`. +beam = RectangularShape(particle_spacing, n_particles_per_dimension, + (0.0, 0.0), density=material.density, tlsph=true) + +solid = union(beam, fixed_particles) +solid.coordinates .+= 0.2 + 0.08 + +# Movement function +movement_function(t) = SVector(0.0, 0.04 * cos(5 * pi * t) - 0.04) + +is_moving(t) = true + +boundary_movement = BoundaryMovement(movement_function, is_moving) + +# ========================================================================================== +# ==== Resolution +fluid_particle_spacing = particle_spacing + +# Change spacing ratio to 3 and boundary layers to 1 when using Monaghan-Kajtar boundary model +boundary_layers = 3 +spacing_ratio = 1 + +# ========================================================================================== +# ==== Experiment Setup +tspan = (0.0, 2.0) + +# Boundary geometry and initial fluid particle positions +tank_size = (1.0, 0.5) +initial_fluid_size = tank_size +initial_velocity = (0.0, 0.0) + +fluid_density = 1000.0 +sound_speed = 10.0 +state_equation = StateEquationCole(; sound_speed, reference_density=fluid_density, + exponent=7, background_pressure=10_000.0) + +tank = RectangularTank(fluid_particle_spacing, initial_fluid_size, tank_size, fluid_density, + n_layers=boundary_layers, spacing_ratio=spacing_ratio, + faces=(false, false, true, true), velocity=initial_velocity) +fluid = setdiff(tank.fluid, solid) + +# ========================================================================================== +# ==== Solid +# The kernel in the reference uses a differently scaled smoothing length, +# so this is equivalent to the smoothing length of `sqrt(2) * particle_spacing` used in the paper. +smoothing_length = 2 * sqrt(2) * particle_spacing +smoothing_kernel = WendlandC2Kernel{2}() + +boundary_density_calculator = AdamiPressureExtrapolation() +viscosity_wall = nothing +# Activate to switch to no-slip walls +#viscosity_wall = ViscosityAdami(nu=0.0025 * smoothing_length * sound_speed / 8) + +# For the FSI we need the hydrodynamic masses and densities in the solid boundary model +hydrodynamic_densites = fluid_density * ones(size(solid.density)) +hydrodynamic_masses = hydrodynamic_densites * particle_spacing^2 + +# boundary_model = BoundaryModelDummyParticles(hydrodynamic_densites, hydrodynamic_masses, +# state_equation=state_equation, +# boundary_density_calculator, +# smoothing_kernel, smoothing_length, +# viscosity=viscosity_wall) +k_solid = 10.0 +beta_solid = fluid_particle_spacing / particle_spacing +boundary_model_solid = BoundaryModelMonaghanKajtar(k_solid, beta_solid, + particle_spacing, + hydrodynamic_masses) + +solid_system = TotalLagrangianSPHSystem(solid, smoothing_kernel, smoothing_length, + material.E, material.nu, + n_fixed_particles=nparticles(fixed_particles), + movement=boundary_movement, + boundary_model=boundary_model_solid, + penalty_force=PenaltyForceGanzenmueller(alpha=0.01)) + +# ========================================================================================== +# ==== Fluid +smoothing_length = 1.2 * fluid_particle_spacing +smoothing_kernel = SchoenbergCubicSplineKernel{2}() + +fluid_density_calculator = ContinuityDensity() +viscosity = ArtificialViscosityMonaghan(alpha=0.02, beta=0.0) +density_diffusion = DensityDiffusionMolteniColagrossi(delta=0.1) + +fluid_system = WeaklyCompressibleSPHSystem(fluid, fluid_density_calculator, + state_equation, smoothing_kernel, + smoothing_length, viscosity=viscosity, + density_diffusion=density_diffusion) + +# ========================================================================================== +# ==== Boundary +boundary_density_calculator = AdamiPressureExtrapolation() +viscosity_wall = nothing +# Activate to switch to no-slip walls +#viscosity_wall = ViscosityAdami(nu=0.0025 * smoothing_length * sound_speed / 8) + +boundary_model = BoundaryModelDummyParticles(tank.boundary.density, tank.boundary.mass, + state_equation=state_equation, + boundary_density_calculator, + smoothing_kernel, smoothing_length, + viscosity=viscosity_wall) + +boundary_system = BoundarySPHSystem(tank.boundary, boundary_model) + +# ========================================================================================== +# ==== Simulation +periodic_box = PeriodicBox(min_corner=[0.0, -0.25], max_corner=[1.0, 0.75]) +neighborhood_search = GridNeighborhoodSearch{2}(; periodic_box) + +semi = Semidiscretization(fluid_system, boundary_system, solid_system; neighborhood_search) +ode = semidiscretize(semi, tspan) + +info_callback = InfoCallback(interval=100) +saving_callback = SolutionSavingCallback(dt=0.02, prefix="") + +callbacks = CallbackSet(info_callback, saving_callback) + +# Use a Runge-Kutta method with automatic (error based) time step size control. +# Limiting of the maximum stepsize is necessary to prevent crashing. +# When particles are approaching a wall in a uniform way, they can be advanced +# with large time steps. Close to the wall, the stepsize has to be reduced drastically. +# Sometimes, the method fails to do so because forces become extremely large when +# fluid particles are very close to boundary particles, and the time integration method +# interprets this as an instability. +sol = solve(ode, RDPK3SpFSAL35(), + abstol=1e-8, # Default abstol is 1e-6 (may need to be tuned to prevent boundary penetration) + reltol=1e-4, # Default reltol is 1e-3 (may need to be tuned to prevent boundary penetration) + dtmax=1e-2, # Limit stepsize to prevent crashing + save_everystep=false, callback=callbacks); From 14e3f3dfeff0ae63105b54dbcc773dba284217fa Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Wed, 6 Nov 2024 13:42:20 +0100 Subject: [PATCH 086/134] Fix fin simulation --- examples/fsi/fin_2d.jl | 50 +++++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/examples/fsi/fin_2d.jl b/examples/fsi/fin_2d.jl index 41f7d7098f..ea42a14fa6 100644 --- a/examples/fsi/fin_2d.jl +++ b/examples/fsi/fin_2d.jl @@ -8,10 +8,10 @@ n_particles_y = 3 # ========================================================================================== # ==== Experiment Setup gravity = 2.0 -tspan = (0.0, 5.0) +tspan = (0.0, 2.0) elastic_beam = (length=0.45, thickness=0.02) -material = (density=1000.0, E=1.0e7, nu=0.4) +material = (density=1000.0, E=5.0e7, nu=0.4) clamp_radius = 0.05 # The structure starts at the position of the first particle and ends @@ -38,10 +38,11 @@ beam = RectangularShape(particle_spacing, n_particles_per_dimension, (0.0, 0.0), density=material.density, tlsph=true) solid = union(beam, fixed_particles) -solid.coordinates .+= 0.2 + 0.08 +movement_amplitude = 0.1 +solid.coordinates .+= 0.4 - 0.5 * elastic_beam.thickness #+ movement_amplitude # Movement function -movement_function(t) = SVector(0.0, 0.04 * cos(5 * pi * t) - 0.04) +movement_function(t) = SVector(0.0, movement_amplitude * sin(5 * pi * t) )#- movement_amplitude) is_moving(t) = true @@ -52,20 +53,21 @@ boundary_movement = BoundaryMovement(movement_function, is_moving) fluid_particle_spacing = particle_spacing # Change spacing ratio to 3 and boundary layers to 1 when using Monaghan-Kajtar boundary model -boundary_layers = 3 +boundary_layers = 4 spacing_ratio = 1 # ========================================================================================== # ==== Experiment Setup -tspan = (0.0, 2.0) +# tspan = (0.0, 2.0) # Boundary geometry and initial fluid particle positions -tank_size = (1.0, 0.5) +tank_size = (2.0, 0.8) initial_fluid_size = tank_size initial_velocity = (0.0, 0.0) fluid_density = 1000.0 -sound_speed = 10.0 +nu = 0.1 / fluid_density # viscosity parameter +sound_speed = 20.0 state_equation = StateEquationCole(; sound_speed, reference_density=fluid_density, exponent=7, background_pressure=10_000.0) @@ -90,16 +92,16 @@ viscosity_wall = nothing hydrodynamic_densites = fluid_density * ones(size(solid.density)) hydrodynamic_masses = hydrodynamic_densites * particle_spacing^2 -# boundary_model = BoundaryModelDummyParticles(hydrodynamic_densites, hydrodynamic_masses, -# state_equation=state_equation, -# boundary_density_calculator, -# smoothing_kernel, smoothing_length, -# viscosity=viscosity_wall) -k_solid = 10.0 -beta_solid = fluid_particle_spacing / particle_spacing -boundary_model_solid = BoundaryModelMonaghanKajtar(k_solid, beta_solid, - particle_spacing, - hydrodynamic_masses) +boundary_model_solid = BoundaryModelDummyParticles(hydrodynamic_densites, hydrodynamic_masses, + state_equation=state_equation, + boundary_density_calculator, + smoothing_kernel, smoothing_length, + viscosity=viscosity_wall) +# k_solid = 0.1 +# beta_solid = fluid_particle_spacing / particle_spacing +# boundary_model_solid = BoundaryModelMonaghanKajtar(k_solid, beta_solid, +# particle_spacing, +# hydrodynamic_masses) solid_system = TotalLagrangianSPHSystem(solid, smoothing_kernel, smoothing_length, material.E, material.nu, @@ -110,8 +112,8 @@ solid_system = TotalLagrangianSPHSystem(solid, smoothing_kernel, smoothing_lengt # ========================================================================================== # ==== Fluid -smoothing_length = 1.2 * fluid_particle_spacing -smoothing_kernel = SchoenbergCubicSplineKernel{2}() +smoothing_length = 3.5 * fluid_particle_spacing +smoothing_kernel = WendlandC2Kernel{2}() fluid_density_calculator = ContinuityDensity() viscosity = ArtificialViscosityMonaghan(alpha=0.02, beta=0.0) @@ -121,6 +123,9 @@ fluid_system = WeaklyCompressibleSPHSystem(fluid, fluid_density_calculator, state_equation, smoothing_kernel, smoothing_length, viscosity=viscosity, density_diffusion=density_diffusion) +# fluid_system = EntropicallyDampedSPHSystem(fluid, smoothing_kernel, smoothing_length, +# sound_speed, viscosity=ViscosityAdami(; nu), +# transport_velocity=TransportVelocityAdami(10 * sound_speed^2 * fluid_density)) # ========================================================================================== # ==== Boundary @@ -139,7 +144,8 @@ boundary_system = BoundarySPHSystem(tank.boundary, boundary_model) # ========================================================================================== # ==== Simulation -periodic_box = PeriodicBox(min_corner=[0.0, -0.25], max_corner=[1.0, 0.75]) +periodic_box = PeriodicBox(min_corner=[0.0, -0.25], max_corner=[2.0, 1.0]) +# cell_list = TrixiParticles.PointNeighbors.FullGridCellList(min_corner=[0.0, -0.25], max_corner=[1.0, 0.75]) neighborhood_search = GridNeighborhoodSearch{2}(; periodic_box) semi = Semidiscretization(fluid_system, boundary_system, solid_system; neighborhood_search) @@ -148,7 +154,7 @@ ode = semidiscretize(semi, tspan) info_callback = InfoCallback(interval=100) saving_callback = SolutionSavingCallback(dt=0.02, prefix="") -callbacks = CallbackSet(info_callback, saving_callback) +callbacks = CallbackSet(info_callback, saving_callback, UpdateCallback()) # Use a Runge-Kutta method with automatic (error based) time step size control. # Limiting of the maximum stepsize is necessary to prevent crashing. From cf557575d625ee84213cef8fadebf44efb7c3d7f Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Mon, 19 May 2025 15:11:01 +0200 Subject: [PATCH 087/134] Use flexural rigidity --- examples/fsi/fin_2d.jl | 52 ++++++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/examples/fsi/fin_2d.jl b/examples/fsi/fin_2d.jl index ea42a14fa6..55f2cabb90 100644 --- a/examples/fsi/fin_2d.jl +++ b/examples/fsi/fin_2d.jl @@ -8,41 +8,51 @@ n_particles_y = 3 # ========================================================================================== # ==== Experiment Setup gravity = 2.0 -tspan = (0.0, 2.0) +tspan = (0.0, 0.5) + +fin_length=0.5 +fin_thickness=0.02 +flexural_rigidity = 5.0 +poisson_ratio = 0.3 +modulus = 12 * (1 - poisson_ratio^2) * flexural_rigidity / (fin_thickness^3) + +fiber_volume_fraction = 0.6 +fiber_density = 1800.0 +epoxy_density = 1250.0 +density = fiber_volume_fraction * fiber_density + + (1 - fiber_volume_fraction) * epoxy_density -elastic_beam = (length=0.45, thickness=0.02) -material = (density=1000.0, E=5.0e7, nu=0.4) clamp_radius = 0.05 # The structure starts at the position of the first particle and ends # at the position of the last particle. -particle_spacing = elastic_beam.thickness / (n_particles_y - 1) +particle_spacing = fin_thickness / (n_particles_y - 1) # Add particle_spacing/2 to the clamp_radius to ensure that particles are also placed on the radius fixed_particles = SphereShape(particle_spacing, clamp_radius + particle_spacing / 2, - (0.0, elastic_beam.thickness / 2), material.density, + (0.0, fin_thickness / 2), density, cutout_min=(0.0, 0.0), - cutout_max=(clamp_radius, elastic_beam.thickness), + cutout_max=(clamp_radius, fin_thickness), tlsph=true) n_particles_clamp_x = round(Int, clamp_radius / particle_spacing) # Beam and clamped particles -n_particles_per_dimension = (round(Int, elastic_beam.length / particle_spacing) + +n_particles_per_dimension = (round(Int, fin_length / particle_spacing) + n_particles_clamp_x + 1, n_particles_y) # Note that the `RectangularShape` puts the first particle half a particle spacing away # from the boundary, which is correct for fluids, but not for solids. # We therefore need to pass `tlsph=true`. beam = RectangularShape(particle_spacing, n_particles_per_dimension, - (0.0, 0.0), density=material.density, tlsph=true) + (0.0, 0.0), density=density, tlsph=true) solid = union(beam, fixed_particles) movement_amplitude = 0.1 -solid.coordinates .+= 0.4 - 0.5 * elastic_beam.thickness #+ movement_amplitude +solid.coordinates .+= 0.4 - 0.5 * fin_thickness #+ movement_amplitude # Movement function -movement_function(t) = SVector(0.0, movement_amplitude * sin(5 * pi * t) )#- movement_amplitude) +movement_function(t) = SVector(0.0, movement_amplitude * sin(5 * pi * t))#- movement_amplitude) is_moving(t) = true @@ -80,7 +90,7 @@ fluid = setdiff(tank.fluid, solid) # ==== Solid # The kernel in the reference uses a differently scaled smoothing length, # so this is equivalent to the smoothing length of `sqrt(2) * particle_spacing` used in the paper. -smoothing_length = 2 * sqrt(2) * particle_spacing +smoothing_length = sqrt(2) * particle_spacing smoothing_kernel = WendlandC2Kernel{2}() boundary_density_calculator = AdamiPressureExtrapolation() @@ -92,11 +102,12 @@ viscosity_wall = nothing hydrodynamic_densites = fluid_density * ones(size(solid.density)) hydrodynamic_masses = hydrodynamic_densites * particle_spacing^2 -boundary_model_solid = BoundaryModelDummyParticles(hydrodynamic_densites, hydrodynamic_masses, - state_equation=state_equation, - boundary_density_calculator, - smoothing_kernel, smoothing_length, - viscosity=viscosity_wall) +boundary_model_solid = BoundaryModelDummyParticles(hydrodynamic_densites, + hydrodynamic_masses, + state_equation=state_equation, + boundary_density_calculator, + smoothing_kernel, smoothing_length, + viscosity=viscosity_wall) # k_solid = 0.1 # beta_solid = fluid_particle_spacing / particle_spacing # boundary_model_solid = BoundaryModelMonaghanKajtar(k_solid, beta_solid, @@ -104,7 +115,7 @@ boundary_model_solid = BoundaryModelDummyParticles(hydrodynamic_densites, hydrod # hydrodynamic_masses) solid_system = TotalLagrangianSPHSystem(solid, smoothing_kernel, smoothing_length, - material.E, material.nu, + modulus, poisson_ratio, n_fixed_particles=nparticles(fixed_particles), movement=boundary_movement, boundary_model=boundary_model_solid, @@ -112,7 +123,7 @@ solid_system = TotalLagrangianSPHSystem(solid, smoothing_kernel, smoothing_lengt # ========================================================================================== # ==== Fluid -smoothing_length = 3.5 * fluid_particle_spacing +smoothing_length = 2 * fluid_particle_spacing smoothing_kernel = WendlandC2Kernel{2}() fluid_density_calculator = ContinuityDensity() @@ -122,7 +133,8 @@ density_diffusion = DensityDiffusionMolteniColagrossi(delta=0.1) fluid_system = WeaklyCompressibleSPHSystem(fluid, fluid_density_calculator, state_equation, smoothing_kernel, smoothing_length, viscosity=viscosity, - density_diffusion=density_diffusion) + density_diffusion=density_diffusion, + pressure_acceleration=tensile_instability_control) # fluid_system = EntropicallyDampedSPHSystem(fluid, smoothing_kernel, smoothing_length, # sound_speed, viscosity=ViscosityAdami(; nu), # transport_velocity=TransportVelocityAdami(10 * sound_speed^2 * fluid_density)) @@ -154,7 +166,7 @@ ode = semidiscretize(semi, tspan) info_callback = InfoCallback(interval=100) saving_callback = SolutionSavingCallback(dt=0.02, prefix="") -callbacks = CallbackSet(info_callback, saving_callback, UpdateCallback()) +callbacks = CallbackSet(info_callback, saving_callback, ParticleShiftingCallback()) # Use a Runge-Kutta method with automatic (error based) time step size control. # Limiting of the maximum stepsize is necessary to prevent crashing. From 82ff34497903967bb1ea889fe703ef09ee45bc70 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Tue, 20 May 2025 00:28:36 +0200 Subject: [PATCH 088/134] Add rotation angle --- examples/fsi/fin_2d.jl | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/examples/fsi/fin_2d.jl b/examples/fsi/fin_2d.jl index 55f2cabb90..5c4647d802 100644 --- a/examples/fsi/fin_2d.jl +++ b/examples/fsi/fin_2d.jl @@ -10,8 +10,8 @@ n_particles_y = 3 gravity = 2.0 tspan = (0.0, 0.5) -fin_length=0.5 -fin_thickness=0.02 +fin_length = 0.5 +fin_thickness = 0.02 flexural_rigidity = 5.0 poisson_ratio = 0.3 modulus = 12 * (1 - poisson_ratio^2) * flexural_rigidity / (fin_thickness^3) @@ -48,11 +48,23 @@ beam = RectangularShape(particle_spacing, n_particles_per_dimension, (0.0, 0.0), density=density, tlsph=true) solid = union(beam, fixed_particles) -movement_amplitude = 0.1 -solid.coordinates .+= 0.4 - 0.5 * fin_thickness #+ movement_amplitude +solid.coordinates .+= 0.4 - fin_thickness / 2 # Movement function -movement_function(t) = SVector(0.0, movement_amplitude * sin(5 * pi * t))#- movement_amplitude) +const FREQUENCY = 1.3 # Hz +const AMPLITUDE = 0.1 +rotation_deg = 10 # degrees +const CENTER = (0.0, 0.4) +const ROTATION_ANGLE = rotation_deg * pi / 180 +@inline function movement_function(x, t) + sin_scaled = sin(FREQUENCY * 2pi * t) + translation = SVector(0.0, AMPLITUDE * sin_scaled) + x_centered = x .- CENTER + angle = ROTATION_ANGLE * sin_scaled + rotated = SVector(x_centered[1] * cos(angle) - x_centered[2] * sin(angle), + x_centered[1] * sin(angle) + x_centered[2] * cos(angle)) + return rotated .+ CENTER .+ translation +end is_moving(t) = true @@ -94,9 +106,7 @@ smoothing_length = sqrt(2) * particle_spacing smoothing_kernel = WendlandC2Kernel{2}() boundary_density_calculator = AdamiPressureExtrapolation() -viscosity_wall = nothing -# Activate to switch to no-slip walls -#viscosity_wall = ViscosityAdami(nu=0.0025 * smoothing_length * sound_speed / 8) +viscosity_fin = ViscosityAdami(nu=1e-6) # For the FSI we need the hydrodynamic masses and densities in the solid boundary model hydrodynamic_densites = fluid_density * ones(size(solid.density)) @@ -107,7 +117,7 @@ boundary_model_solid = BoundaryModelDummyParticles(hydrodynamic_densites, state_equation=state_equation, boundary_density_calculator, smoothing_kernel, smoothing_length, - viscosity=viscosity_wall) + viscosity=viscosity_fin) # k_solid = 0.1 # beta_solid = fluid_particle_spacing / particle_spacing # boundary_model_solid = BoundaryModelMonaghanKajtar(k_solid, beta_solid, @@ -142,15 +152,10 @@ fluid_system = WeaklyCompressibleSPHSystem(fluid, fluid_density_calculator, # ========================================================================================== # ==== Boundary boundary_density_calculator = AdamiPressureExtrapolation() -viscosity_wall = nothing -# Activate to switch to no-slip walls -#viscosity_wall = ViscosityAdami(nu=0.0025 * smoothing_length * sound_speed / 8) - boundary_model = BoundaryModelDummyParticles(tank.boundary.density, tank.boundary.mass, state_equation=state_equation, boundary_density_calculator, - smoothing_kernel, smoothing_length, - viscosity=viscosity_wall) + smoothing_kernel, smoothing_length) boundary_system = BoundarySPHSystem(tank.boundary, boundary_model) From f7bd53aff05696ba40d1f0dcc1e6bd3882731542 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Mon, 26 May 2025 14:51:27 +0200 Subject: [PATCH 089/134] Update fin example --- examples/fsi/fin_2d.jl | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/examples/fsi/fin_2d.jl b/examples/fsi/fin_2d.jl index 5c4647d802..a41eba4515 100644 --- a/examples/fsi/fin_2d.jl +++ b/examples/fsi/fin_2d.jl @@ -1,5 +1,5 @@ using TrixiParticles -using OrdinaryDiffEq +using OrdinaryDiffEqLowStorageRK # ========================================================================================== # ==== Resolution @@ -8,10 +8,10 @@ n_particles_y = 3 # ========================================================================================== # ==== Experiment Setup gravity = 2.0 -tspan = (0.0, 0.5) +tspan = (0.0, 5.0) -fin_length = 0.5 -fin_thickness = 0.02 +fin_length = 0.6 +fin_thickness = 10e-3 flexural_rigidity = 5.0 poisson_ratio = 0.3 modulus = 12 * (1 - poisson_ratio^2) * flexural_rigidity / (fin_thickness^3) @@ -24,15 +24,20 @@ density = fiber_volume_fraction * fiber_density + clamp_radius = 0.05 +tank_size = (2.0, 1.0) +const CENTER = (tank_size[2] / 2, tank_size[2] / 2) +initial_fluid_size = tank_size +initial_velocity = (1.0, 0.0) + # The structure starts at the position of the first particle and ends # at the position of the last particle. particle_spacing = fin_thickness / (n_particles_y - 1) # Add particle_spacing/2 to the clamp_radius to ensure that particles are also placed on the radius fixed_particles = SphereShape(particle_spacing, clamp_radius + particle_spacing / 2, - (0.0, fin_thickness / 2), density, - cutout_min=(0.0, 0.0), - cutout_max=(clamp_radius, fin_thickness), + CENTER, density, + cutout_min=(CENTER[1], CENTER[2] - fin_thickness / 2), + cutout_max=CENTER .+ (clamp_radius, fin_thickness / 2), tlsph=true) n_particles_clamp_x = round(Int, clamp_radius / particle_spacing) @@ -45,16 +50,14 @@ n_particles_per_dimension = (round(Int, fin_length / particle_spacing) + # from the boundary, which is correct for fluids, but not for solids. # We therefore need to pass `tlsph=true`. beam = RectangularShape(particle_spacing, n_particles_per_dimension, - (0.0, 0.0), density=density, tlsph=true) + (CENTER[1], CENTER[2] - fin_thickness / 2), density=density, tlsph=true) solid = union(beam, fixed_particles) -solid.coordinates .+= 0.4 - fin_thickness / 2 # Movement function const FREQUENCY = 1.3 # Hz -const AMPLITUDE = 0.1 -rotation_deg = 10 # degrees -const CENTER = (0.0, 0.4) +const AMPLITUDE = 0.15 # m +rotation_deg = 12 # degrees const ROTATION_ANGLE = rotation_deg * pi / 180 @inline function movement_function(x, t) sin_scaled = sin(FREQUENCY * 2pi * t) @@ -82,11 +85,6 @@ spacing_ratio = 1 # ==== Experiment Setup # tspan = (0.0, 2.0) -# Boundary geometry and initial fluid particle positions -tank_size = (2.0, 0.8) -initial_fluid_size = tank_size -initial_velocity = (0.0, 0.0) - fluid_density = 1000.0 nu = 0.1 / fluid_density # viscosity parameter sound_speed = 20.0 @@ -161,7 +159,9 @@ boundary_system = BoundarySPHSystem(tank.boundary, boundary_model) # ========================================================================================== # ==== Simulation -periodic_box = PeriodicBox(min_corner=[0.0, -0.25], max_corner=[2.0, 1.0]) +min_corner = minimum(tank.boundary.coordinates, dims=2) .- fluid_particle_spacing / 2 +max_corner = maximum(tank.boundary.coordinates, dims=2) .+ fluid_particle_spacing / 2 +periodic_box = PeriodicBox(; min_corner, max_corner) # cell_list = TrixiParticles.PointNeighbors.FullGridCellList(min_corner=[0.0, -0.25], max_corner=[1.0, 0.75]) neighborhood_search = GridNeighborhoodSearch{2}(; periodic_box) @@ -184,4 +184,4 @@ sol = solve(ode, RDPK3SpFSAL35(), abstol=1e-8, # Default abstol is 1e-6 (may need to be tuned to prevent boundary penetration) reltol=1e-4, # Default reltol is 1e-3 (may need to be tuned to prevent boundary penetration) dtmax=1e-2, # Limit stepsize to prevent crashing - save_everystep=false, callback=callbacks); + save_everystep=false, callback=callbacks, maxiters=10^8); From ae0230d855a751df07b51690d676b25e37830ad2 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Wed, 28 May 2025 18:08:29 +0200 Subject: [PATCH 090/134] Move movement code to TrixiParticles module --- examples/fsi/fin_2d.jl | 39 +++++++++++++++++---------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/examples/fsi/fin_2d.jl b/examples/fsi/fin_2d.jl index a41eba4515..cd8419f834 100644 --- a/examples/fsi/fin_2d.jl +++ b/examples/fsi/fin_2d.jl @@ -25,7 +25,7 @@ density = fiber_volume_fraction * fiber_density + clamp_radius = 0.05 tank_size = (2.0, 1.0) -const CENTER = (tank_size[2] / 2, tank_size[2] / 2) +center = (tank_size[2] / 2, tank_size[2] / 2) initial_fluid_size = tank_size initial_velocity = (1.0, 0.0) @@ -35,9 +35,9 @@ particle_spacing = fin_thickness / (n_particles_y - 1) # Add particle_spacing/2 to the clamp_radius to ensure that particles are also placed on the radius fixed_particles = SphereShape(particle_spacing, clamp_radius + particle_spacing / 2, - CENTER, density, - cutout_min=(CENTER[1], CENTER[2] - fin_thickness / 2), - cutout_max=CENTER .+ (clamp_radius, fin_thickness / 2), + center, density, + cutout_min=(center[1], center[2] - fin_thickness / 2), + cutout_max=center .+ (clamp_radius, fin_thickness / 2), tlsph=true) n_particles_clamp_x = round(Int, clamp_radius / particle_spacing) @@ -50,28 +50,21 @@ n_particles_per_dimension = (round(Int, fin_length / particle_spacing) + # from the boundary, which is correct for fluids, but not for solids. # We therefore need to pass `tlsph=true`. beam = RectangularShape(particle_spacing, n_particles_per_dimension, - (CENTER[1], CENTER[2] - fin_thickness / 2), density=density, tlsph=true) + (center[1], center[2] - fin_thickness / 2), density=density, + tlsph=true) solid = union(beam, fixed_particles) # Movement function -const FREQUENCY = 1.3 # Hz -const AMPLITUDE = 0.15 # m +frequency = 1.3 # Hz +amplitude = 0.15 # m rotation_deg = 12 # degrees -const ROTATION_ANGLE = rotation_deg * pi / 180 -@inline function movement_function(x, t) - sin_scaled = sin(FREQUENCY * 2pi * t) - translation = SVector(0.0, AMPLITUDE * sin_scaled) - x_centered = x .- CENTER - angle = ROTATION_ANGLE * sin_scaled - rotated = SVector(x_centered[1] * cos(angle) - x_centered[2] * sin(angle), - x_centered[1] * sin(angle) + x_centered[2] * cos(angle)) - return rotated .+ CENTER .+ translation -end +translation_vector = SVector(0.0, amplitude) +rotation_angle = rotation_deg * pi / 180 -is_moving(t) = true - -boundary_movement = BoundaryMovement(movement_function, is_moving) +boundary_movement = TrixiParticles.oscillating_movement(frequency, + SVector(0.0, amplitude), + rotation_angle, center) # ========================================================================================== # ==== Resolution @@ -162,7 +155,7 @@ boundary_system = BoundarySPHSystem(tank.boundary, boundary_model) min_corner = minimum(tank.boundary.coordinates, dims=2) .- fluid_particle_spacing / 2 max_corner = maximum(tank.boundary.coordinates, dims=2) .+ fluid_particle_spacing / 2 periodic_box = PeriodicBox(; min_corner, max_corner) -# cell_list = TrixiParticles.PointNeighbors.FullGridCellList(min_corner=[0.0, -0.25], max_corner=[1.0, 0.75]) +cell_list = FullGridCellList(; min_corner, max_corner) neighborhood_search = GridNeighborhoodSearch{2}(; periodic_box) semi = Semidiscretization(fluid_system, boundary_system, solid_system; neighborhood_search) @@ -171,7 +164,9 @@ ode = semidiscretize(semi, tspan) info_callback = InfoCallback(interval=100) saving_callback = SolutionSavingCallback(dt=0.02, prefix="") -callbacks = CallbackSet(info_callback, saving_callback, ParticleShiftingCallback()) +split_integration = SplitIntegrationCallback(RDPK3SpFSAL35()) +callbacks = CallbackSet(info_callback, saving_callback, ParticleShiftingCallback(), + split_integration) # Use a Runge-Kutta method with automatic (error based) time step size control. # Limiting of the maximum stepsize is necessary to prevent crashing. From d2e47e9ae05c2b477b0ff78f16d7ba69937f908e Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Fri, 6 Jun 2025 11:17:16 +0200 Subject: [PATCH 091/134] Fix complex shape offset --- src/setups/complex_shape.jl | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/setups/complex_shape.jl b/src/setups/complex_shape.jl index 6a78b412e2..fd7e821ad1 100644 --- a/src/setups/complex_shape.jl +++ b/src/setups/complex_shape.jl @@ -46,16 +46,12 @@ function ComplexShape(geometry; particle_spacing, density, point_in_geometry_algorithm=WindingNumberJacobson(; geometry, hierarchical_winding=true, winding_number_factor=sqrt(eps())), - store_winding_number=false, grid_offset::Real=0.0, + store_winding_number=false, grid_offset=0.0, max_nparticles=10^7, pad_initial_particle_grid=2particle_spacing) if ndims(geometry) == 3 && point_in_geometry_algorithm isa WindingNumberHormann throw(ArgumentError("`WindingNumberHormann` only supports 2D geometries")) end - if grid_offset < 0.0 - throw(ArgumentError("only a positive `grid_offset` is supported")) - end - grid = particle_grid(geometry, particle_spacing; padding=pad_initial_particle_grid, grid_offset, max_nparticles) @@ -145,7 +141,12 @@ function particle_grid(geometry, particle_spacing; padding=2particle_spacing, grid_offset=0.0, max_nparticles=10^7) (; max_corner) = geometry + # First subtract the grid offset, then add it again after rounding. + # This is making sure that `min_corner` is `n * particle_spacing` + # away from `grid_offset`, so that a particle is placed at `grid_offset`. min_corner = geometry.min_corner .- grid_offset .- padding + min_corner = floor.(Int, min_corner ./ particle_spacing) .* particle_spacing + min_corner = min_corner .+ grid_offset n_particles_per_dimension = Tuple(ceil.(Int, (max_corner .- min_corner .+ 2padding) ./ From 2d10005c07642067c37c7543f1ba6d0e24271b2b Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Fri, 6 Jun 2025 11:21:19 +0200 Subject: [PATCH 092/134] Model foot pocket --- examples/fsi/fin_2d.jl | 120 +++++++++++++++++++++++++++++++---------- 1 file changed, 92 insertions(+), 28 deletions(-) diff --git a/examples/fsi/fin_2d.jl b/examples/fsi/fin_2d.jl index cd8419f834..200a8aa535 100644 --- a/examples/fsi/fin_2d.jl +++ b/examples/fsi/fin_2d.jl @@ -1,5 +1,6 @@ using TrixiParticles using OrdinaryDiffEqLowStorageRK +using OrdinaryDiffEqSymplecticRK # ========================================================================================== # ==== Resolution @@ -8,11 +9,11 @@ n_particles_y = 3 # ========================================================================================== # ==== Experiment Setup gravity = 2.0 -tspan = (0.0, 5.0) +tspan = (0.0, 3.0) fin_length = 0.6 fin_thickness = 10e-3 -flexural_rigidity = 5.0 +flexural_rigidity = 6.0 poisson_ratio = 0.3 modulus = 12 * (1 - poisson_ratio^2) * flexural_rigidity / (fin_thickness^3) @@ -34,37 +35,94 @@ initial_velocity = (1.0, 0.0) particle_spacing = fin_thickness / (n_particles_y - 1) # Add particle_spacing/2 to the clamp_radius to ensure that particles are also placed on the radius -fixed_particles = SphereShape(particle_spacing, clamp_radius + particle_spacing / 2, - center, density, - cutout_min=(center[1], center[2] - fin_thickness / 2), - cutout_max=center .+ (clamp_radius, fin_thickness / 2), - tlsph=true) +# fixed_particles = SphereShape(particle_spacing, clamp_radius + particle_spacing / 2, +# (center[1] - clamp_radius - particle_spacing, center[2] + fin_thickness / 2), density, +# # cutout_min=(center[1], center[2] - fin_thickness / 2), +# # cutout_max=center .+ (clamp_radius, fin_thickness / 2), +# tlsph=true) -n_particles_clamp_x = round(Int, clamp_radius / particle_spacing) +# fixed_beam = RectangularShape(particle_spacing, (round(Int, 0.05 / particle_spacing) + 1,# + n_particles_clamp_x, +# n_particles_y), +# center, density=density, tlsph=true) + +# fixed_particles = union(fixed_particles, fixed_beam) + +# n_particles_clamp_x = round(Int, clamp_radius / particle_spacing) + +using TrixiParticles +using PythonCall +using CondaPkg + +CondaPkg.add("svgpathtools") +CondaPkg.add("ezdxf") +svgpath = pyimport("svgpathtools") + +svg_path = "M509.299 100.016C507.966 91.5871 505.915 85.7111 503.145 82.3879C498.991 77.4031 479.883 60.7871 475.521 58.2947C471.16 55.8023 455.167 49.1559 448.936 47.702C442.705 46.2481 355.471 30.463 339.893 28.8014C329.508 27.6937 287.761 23.5397 214.651 16.3394C199.727 15.7768 185.488 15.1656 171.934 14.5056C151.602 13.5157 106.318 5.19544 82.4982 0.830064C58.6781 -3.53532 3.26262 9.37214 0.279574 38.2664C-2.54326 65.6089 16.5085 89.4186 34.9345 99.2843C49.9801 107.34 58.7166 107.403 71.5338 114.275C101.912 130.564 100.849 169.8 123.353 175.939C145.856 182.078 146.637 180.9 155.986 179.279C162.22 178.199 199.267 168.32 267.129 149.644L359.355 117.398L405.801 102.863C446.849 101.008 472.412 100.061 482.489 100.022C497.605 99.9635 507.524 100.062 509.299 100.016Z" + +path = svgpath.parse_path(svg_path) +t_range = range(0, 1, length=50 * length(path)) +points = [(pyconvert(Float64, p.real), -pyconvert(Float64, p.imag)) + for p in (path.point(t) for t in t_range)] + +# ezdxf = pyimport("ezdxf") +# doc = ezdxf.new(dxfversion="R2010") +# msp = doc.modelspace() +# msp.add_polyline2d(points) +# doc.saveas("output.dxf") + +points_matrix = reinterpret(reshape, Float64, points) +scaling = 0.3 / maximum(points_matrix, dims=2)[1] +points_matrix .*= scaling +points_matrix .+= (-0.3, -points_matrix[2, 1]) .+ center .- (0.0, 1e-4) +geometry = TrixiParticles.Polygon(points_matrix) + +# trixi2vtk(geometry) + +point_in_geometry_algorithm = WindingNumberJacobson(; geometry, + winding_number_factor=0.4, + hierarchical_winding=true) + +# Returns `InitialCondition` +shape_sampled = ComplexShape(geometry; particle_spacing, density=density, + store_winding_number=true, grid_offset=center, + point_in_geometry_algorithm) + +fixed_particles = shape_sampled.initial_condition + +# trixi2vtk(shape_sampled.initial_condition) + +# coordinates = stack(shape_sampled.grid) +# trixi2vtk(shape_sampled.signed_distance_field) +# trixi2vtk(coordinates, w=shape_sampled.winding_numbers) + +# Plot the winding number field +# plot(InitialCondition(; coordinates, density=1.0, particle_spacing), +# zcolor=shape_sampled.winding_numbers) # Beam and clamped particles -n_particles_per_dimension = (round(Int, fin_length / particle_spacing) + - n_particles_clamp_x + 1, n_particles_y) +n_particles_per_dimension = (round(Int, fin_length / particle_spacing) + 1,# + n_particles_clamp_x, + n_particles_y) # Note that the `RectangularShape` puts the first particle half a particle spacing away # from the boundary, which is correct for fluids, but not for solids. # We therefore need to pass `tlsph=true`. beam = RectangularShape(particle_spacing, n_particles_per_dimension, - (center[1], center[2] - fin_thickness / 2), density=density, - tlsph=true) + center, density=density, tlsph=true) solid = union(beam, fixed_particles) # Movement function frequency = 1.3 # Hz -amplitude = 0.15 # m -rotation_deg = 12 # degrees +amplitude = 0.18 # m +rotation_deg = 25 # degrees +rotation_phase_offset = 0.12 # periods translation_vector = SVector(0.0, amplitude) rotation_angle = rotation_deg * pi / 180 boundary_movement = TrixiParticles.oscillating_movement(frequency, SVector(0.0, amplitude), - rotation_angle, center) + rotation_angle, center; + rotation_phase_offset, ramp_up=0.5) # ========================================================================================== # ==== Resolution @@ -97,7 +155,7 @@ smoothing_length = sqrt(2) * particle_spacing smoothing_kernel = WendlandC2Kernel{2}() boundary_density_calculator = AdamiPressureExtrapolation() -viscosity_fin = ViscosityAdami(nu=1e-6) +viscosity = ViscosityAdami(nu=1e-6) # For the FSI we need the hydrodynamic masses and densities in the solid boundary model hydrodynamic_densites = fluid_density * ones(size(solid.density)) @@ -108,19 +166,19 @@ boundary_model_solid = BoundaryModelDummyParticles(hydrodynamic_densites, state_equation=state_equation, boundary_density_calculator, smoothing_kernel, smoothing_length, - viscosity=viscosity_fin) + viscosity=viscosity) # k_solid = 0.1 # beta_solid = fluid_particle_spacing / particle_spacing # boundary_model_solid = BoundaryModelMonaghanKajtar(k_solid, beta_solid, # particle_spacing, # hydrodynamic_masses) +n_fixed_particles = nparticles(solid) - nparticles(beam) solid_system = TotalLagrangianSPHSystem(solid, smoothing_kernel, smoothing_length, - modulus, poisson_ratio, - n_fixed_particles=nparticles(fixed_particles), - movement=boundary_movement, + modulus, poisson_ratio; + n_fixed_particles, movement=boundary_movement, boundary_model=boundary_model_solid, - penalty_force=PenaltyForceGanzenmueller(alpha=0.01)) + penalty_force=PenaltyForceGanzenmueller(alpha=0.1)) # ========================================================================================== # ==== Fluid @@ -128,7 +186,6 @@ smoothing_length = 2 * fluid_particle_spacing smoothing_kernel = WendlandC2Kernel{2}() fluid_density_calculator = ContinuityDensity() -viscosity = ArtificialViscosityMonaghan(alpha=0.02, beta=0.0) density_diffusion = DensityDiffusionMolteniColagrossi(delta=0.1) fluid_system = WeaklyCompressibleSPHSystem(fluid, fluid_density_calculator, @@ -164,9 +221,12 @@ ode = semidiscretize(semi, tspan) info_callback = InfoCallback(interval=100) saving_callback = SolutionSavingCallback(dt=0.02, prefix="") -split_integration = SplitIntegrationCallback(RDPK3SpFSAL35()) +split_dt = 2e-5 +split_integration = SplitIntegrationCallback(RDPK3SpFSAL35(), adaptive=false, dt=split_dt, + maxiters=10^8) +stepsize_callback = StepsizeCallback(cfl=0.5) callbacks = CallbackSet(info_callback, saving_callback, ParticleShiftingCallback(), - split_integration) + split_integration, stepsize_callback) # Use a Runge-Kutta method with automatic (error based) time step size control. # Limiting of the maximum stepsize is necessary to prevent crashing. @@ -175,8 +235,12 @@ callbacks = CallbackSet(info_callback, saving_callback, ParticleShiftingCallback # Sometimes, the method fails to do so because forces become extremely large when # fluid particles are very close to boundary particles, and the time integration method # interprets this as an instability. -sol = solve(ode, RDPK3SpFSAL35(), - abstol=1e-8, # Default abstol is 1e-6 (may need to be tuned to prevent boundary penetration) - reltol=1e-4, # Default reltol is 1e-3 (may need to be tuned to prevent boundary penetration) - dtmax=1e-2, # Limit stepsize to prevent crashing +# sol = solve(ode, RDPK3SpFSAL35(), +# abstol=1e-8, # Default abstol is 1e-6 (may need to be tuned to prevent boundary penetration) +# reltol=1e-6, # Default reltol is 1e-3 (may need to be tuned to prevent boundary penetration) +# dtmax=1e-2, # Limit stepsize to prevent crashing +# save_everystep=false, callback=callbacks, maxiters=10^8); + +sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), + dt=1.0, # This is overwritten by the stepsize callback save_everystep=false, callback=callbacks, maxiters=10^8); From a344e5781f5c0913486c88c12fe221f56797548c Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Wed, 11 Jun 2025 14:05:00 +0200 Subject: [PATCH 093/134] Fix kernel for FSI --- examples/fluid/vortex_street.jl | 193 +++++++++++++++ examples/fluid/vortex_street2.jl | 233 ++++++++++++++++++ examples/fluid/vortex_street_postprocess.jl | 168 +++++++++++++ .../structure/total_lagrangian_sph/rhs.jl | 2 + 4 files changed, 596 insertions(+) create mode 100644 examples/fluid/vortex_street.jl create mode 100644 examples/fluid/vortex_street2.jl create mode 100644 examples/fluid/vortex_street_postprocess.jl diff --git a/examples/fluid/vortex_street.jl b/examples/fluid/vortex_street.jl new file mode 100644 index 0000000000..fc33e9b994 --- /dev/null +++ b/examples/fluid/vortex_street.jl @@ -0,0 +1,193 @@ +using TrixiParticles +using OrdinaryDiffEqLowStorageRK + +# ========================================================================================== +# ==== Resolution +fluid_particle_spacing = 0.005 + +# Change spacing ratio to 3 and boundary layers to 1 when using Monaghan-Kajtar boundary model +boundary_layers = 4 +spacing_ratio = 1 + +# ========================================================================================== +# ==== Experiment Setup +# Boundary geometry and initial fluid particle positions +tank_size = (1.0, 0.5) +# tank_size = (0.6, 0.6) +initial_fluid_size = tank_size + +Re = 10000 +initial_velocity = (0.0, 0.0) +nu = 1 * 1 / Re + +tspan = (0.0, 2.0) + +fluid_density = 1000.0 +sound_speed = 50.0 +state_equation = StateEquationCole(; sound_speed, reference_density=fluid_density, + exponent=1, background_pressure=0.0) + +tank = RectangularTank(fluid_particle_spacing, initial_fluid_size, tank_size, fluid_density, + n_layers=boundary_layers, spacing_ratio=spacing_ratio, + faces=(false, false, true, true), velocity=initial_velocity) + +center = tank_size ./ 2 +hollow_sphere = SphereShape(fluid_particle_spacing, 0.1, center, fluid_density, + n_layers=4, sphere_type=RoundSphere()) + +filled_sphere = SphereShape(fluid_particle_spacing, 0.1, center, fluid_density, + sphere_type=RoundSphere()) + +# hollow_sphere = RectangularShape(fluid_particle_spacing, round.(Int, (0.1, 0.3) ./ fluid_particle_spacing), center .- 0.15, +# density=fluid_density) +# filled_sphere = hollow_sphere + +# using PythonCall +# using CondaPkg + +# CondaPkg.add("svgpathtools") +# CondaPkg.add("ezdxf") +# svgpath = pyimport("svgpathtools") + +# svg_path = "M509.299 100.016C507.966 91.5871 505.915 85.7111 503.145 82.3879C498.991 77.4031 479.883 60.7871 475.521 58.2947C471.16 55.8023 455.167 49.1559 448.936 47.702C442.705 46.2481 355.471 30.463 339.893 28.8014C329.508 27.6937 287.761 23.5397 214.651 16.3394C199.727 15.7768 185.488 15.1656 171.934 14.5056C151.602 13.5157 106.318 5.19544 82.4982 0.830064C58.6781 -3.53532 3.26262 9.37214 0.279574 38.2664C-2.54326 65.6089 16.5085 89.4186 34.9345 99.2843C49.9801 107.34 58.7166 107.403 71.5338 114.275C101.912 130.564 100.849 169.8 123.353 175.939C145.856 182.078 146.637 180.9 155.986 179.279C162.22 178.199 199.267 168.32 267.129 149.644L359.355 117.398L405.801 102.863C446.849 101.008 472.412 100.061 482.489 100.022C497.605 99.9635 507.524 100.062 509.299 100.016Z" + +# path = svgpath.parse_path(svg_path) +# t_range = range(0, 1, length=50 * length(path)) +# points = [(pyconvert(Float64, p.real), -pyconvert(Float64, p.imag)) +# for p in (path.point(t) for t in t_range)] + +# # ezdxf = pyimport("ezdxf") +# # doc = ezdxf.new(dxfversion="R2010") +# # msp = doc.modelspace() +# # msp.add_polyline2d(points) +# # doc.saveas("output.dxf") + +# center = tank_size ./ 2 +# points_matrix = reinterpret(reshape, Float64, points) +# scaling = 0.3 / maximum(points_matrix, dims=2)[1] +# points_matrix .*= scaling +# points_matrix .+= (-0.15, -points_matrix[2, 1]) + +# geometry = TrixiParticles.Polygon(points_matrix) + +# # trixi2vtk(geometry) + +# point_in_geometry_algorithm = WindingNumberJacobson(; geometry, +# winding_number_factor=0.4, +# hierarchical_winding=true) + +# # Returns `InitialCondition` +# shape_sampled = ComplexShape(geometry; particle_spacing=fluid_particle_spacing, density=fluid_density, +# store_winding_number=true, +# point_in_geometry_algorithm) + +# # angle = pi / 4 +# # using StaticArrays +# # rotation_matrix = @SMatrix [cos(angle) -sin(angle) +# # sin(angle) cos(angle)] +# # shape_sampled.initial_condition.coordinates .= rotation_matrix * shape_sampled.initial_condition.coordinates +# shape_sampled.initial_condition.coordinates .+= center + +# hollow_sphere = shape_sampled.initial_condition +# filled_sphere = hollow_sphere + +# n_particles = round(Int, 0.12 / fluid_particle_spacing) +# cylinder = RectangularShape(fluid_particle_spacing, (n_particles, n_particles), (0.2 - 1.0, 0.24 - 1.0), density=fluid_density) + +fluid = setdiff(tank.fluid, filled_sphere) + +# ========================================================================================== +# ==== Fluid +smoothing_length = 2 * fluid_particle_spacing +smoothing_kernel = WendlandC2Kernel{2}() + +viscosity_fluid = ViscosityAdami(; nu) +# viscosity_fluid = ArtificialViscosityMonaghan(alpha=0.02, beta=0.0) +viscosity_wall = ViscosityAdami(; nu) +density_diffusion = DensityDiffusionMolteniColagrossi(delta=0.1) +fluid_density_calculator = ContinuityDensity() +fluid_system = WeaklyCompressibleSPHSystem(fluid, fluid_density_calculator, + state_equation, smoothing_kernel, + density_diffusion=density_diffusion, + pressure_acceleration=TrixiParticles.tensile_instability_control, + particle_shifting=TrixiParticles.ParticleShiftingSun2019(5.0), + smoothing_length, viscosity=viscosity_fluid) + +# ========================================================================================== +# ==== Boundary +boundary_density_calculator = AdamiPressureExtrapolation(pressure_offset=10000.0) +boundary_model = BoundaryModelDummyParticles(tank.boundary.density, tank.boundary.mass, + state_equation=state_equation, + boundary_density_calculator, + smoothing_kernel, smoothing_length) + +# Movement function +# https://en.wikipedia.org/wiki/Triangle_wave#Harmonics +# triangle_series(t, N) = 8 / pi^2 * sum(i -> (-1)^i / (2i + 1)^2 * sin(2pi * (2i + 1) * t), 0:(N-1)) +# movement_function(x, t) = x + SVector(0.4 * triangle_series(2 * t, 5), 0.0) +# is_moving(t) = true +# boundary_movement = BoundaryMovement(movement_function, is_moving) + +boundary_movement = TrixiParticles.oscillating_movement(1.0, + SVector(0.4, 0.0), + 0.0, center; + ramp_up=0.5) + +boundary_system = BoundarySPHSystem(tank.boundary, boundary_model) + +boundary_model_cylinder = BoundaryModelDummyParticles(hollow_sphere.density, + hollow_sphere.mass, + state_equation=state_equation, + boundary_density_calculator, + smoothing_kernel, smoothing_length, + viscosity=viscosity_wall) + +boundary_system_cylinder = BoundarySPHSystem(hollow_sphere, boundary_model_cylinder, + movement=boundary_movement) + +# boundary_system_cylinder = TotalLagrangianSPHSystem(hollow_sphere, smoothing_kernel, smoothing_length, +# 1e5, 0.0; +# n_fixed_particles=nparticles(hollow_sphere), movement=boundary_movement, +# boundary_model=boundary_model) + +# ========================================================================================== +# ==== Simulation +min_corner = minimum(fluid.coordinates, dims=2) .- fluid_particle_spacing / 2 +max_corner = maximum(fluid.coordinates, dims=2) .+ fluid_particle_spacing / 2 +periodic_box = PeriodicBox(; min_corner, max_corner) +cell_list = FullGridCellList(; min_corner, max_corner) +neighborhood_search = GridNeighborhoodSearch{2}(; periodic_box, cell_list) + +semi = Semidiscretization(fluid_system, boundary_system_cylinder; + neighborhood_search) +ode = semidiscretize(semi, tspan) + +info_callback = InfoCallback(interval=10) +saving_callback = SolutionSavingCallback(dt=0.01, prefix="") +shifting_callback = ParticleShiftingCallback() + +stepsize_callback = StepsizeCallback(cfl=1.5) + +callbacks = CallbackSet(info_callback, saving_callback, shifting_callback, + stepsize_callback) + +# Use a Runge-Kutta method with automatic (error based) time step size control. +# Limiting of the maximum stepsize is necessary to prevent crashing. +# When particles are approaching a wall in a uniform way, they can be advanced +# with large time steps. Close to the wall, the stepsize has to be reduced drastically. +# Sometimes, the method fails to do so because forces become extremely large when +# fluid particles are very close to boundary particles, and the time integration method +# interprets this as an instability. +# fluid_dt = 1e-3 +# sol = solve(ode, RDPK3SpFSAL49(), +# # abstol=1.0e-6, # Default abstol is 1e-6 (may need to be tuned to prevent boundary penetration) +# # reltol=1.0e-4, # Default reltol is 1e-3 (may need to be tuned to prevent boundary penetration) +# adaptive=false, dt=fluid_dt, +# save_everystep=false, callback=callbacks); + +time_step = 1.0 +sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), + dt=time_step, # This is overwritten by the stepsize callback + save_everystep=false, callback=callbacks); + +# plane = interpolate_plane([0.0, -0.25], [1.0, 0.75], 0.0025, semi, fluid_system, sol) diff --git a/examples/fluid/vortex_street2.jl b/examples/fluid/vortex_street2.jl new file mode 100644 index 0000000000..8f89583693 --- /dev/null +++ b/examples/fluid/vortex_street2.jl @@ -0,0 +1,233 @@ +using TrixiParticles +using OrdinaryDiffEqLowStorageRK + +# ========================================================================================== +# ==== Resolution +fluid_particle_spacing = 0.002 + +# Change spacing ratio to 3 and boundary layers to 1 when using Monaghan-Kajtar boundary model +boundary_layers = 4 +spacing_ratio = 1 + +# ========================================================================================== +# ==== Experiment Setup +# Boundary geometry and initial fluid particle positions +tank_size = (1.0, 0.5) +# tank_size = (0.6, 0.6) +initial_fluid_size = tank_size + +initial_velocity = (1.0, 0.0) +nu = 1e-4 + +tspan = (0.0, 2.0) + +fluid_density = 1000.0 +sound_speed = 20.0 +state_equation = StateEquationCole(; sound_speed, reference_density=fluid_density, + exponent=1, background_pressure=0.0) + +tank = RectangularTank(fluid_particle_spacing, initial_fluid_size, tank_size, fluid_density, + n_layers=boundary_layers, spacing_ratio=spacing_ratio, + faces=(false, false, true, true), velocity=initial_velocity) + +center = tank_size ./ 2 +# hollow_sphere = SphereShape(fluid_particle_spacing, 0.1, center, fluid_density, +# n_layers=4, sphere_type=RoundSphere()) + +# filled_sphere = SphereShape(fluid_particle_spacing, 0.1, center, fluid_density, +# sphere_type=RoundSphere()) + +# fluid = setdiff(tank.fluid, filled_sphere) + + +# ========================================================================================== +# ==== Sample and pack foot pocket +using PythonCall +using CondaPkg + +CondaPkg.add("svgpathtools") +CondaPkg.add("ezdxf") +svgpath = pyimport("svgpathtools") + +svg_path = "M509.299 100.016C507.966 91.5871 505.915 85.7111 503.145 82.3879C498.991 77.4031 479.883 60.7871 475.521 58.2947C471.16 55.8023 455.167 49.1559 448.936 47.702C442.705 46.2481 355.471 30.463 339.893 28.8014C329.508 27.6937 287.761 23.5397 214.651 16.3394C199.727 15.7768 185.488 15.1656 171.934 14.5056C151.602 13.5157 106.318 5.19544 82.4982 0.830064C58.6781 -3.53532 3.26262 9.37214 0.279574 38.2664C-2.54326 65.6089 16.5085 89.4186 34.9345 99.2843C49.9801 107.34 58.7166 107.403 71.5338 114.275C101.912 130.564 100.849 169.8 123.353 175.939C145.856 182.078 146.637 180.9 155.986 179.279C162.22 178.199 199.267 168.32 267.129 149.644L359.355 117.398L405.801 102.863C446.849 101.008 472.412 100.061 482.489 100.022C497.605 99.9635 507.524 100.062 509.299 100.016Z" + +path = svgpath.parse_path(svg_path) +t_range = range(0, 1, length=50 * length(path)) +points = [(pyconvert(Float64, p.real), -pyconvert(Float64, p.imag)) + for p in (path.point(t) for t in t_range)] + +points_matrix = reinterpret(reshape, Float64, points) +scaling = 0.3 / maximum(points_matrix, dims=2)[1] +points_matrix .*= scaling +points_matrix .+= (-0.15, -points_matrix[2, 1]) .+ center .- (0.0, 1e-4) +# # Clamp the blade in one layer of particles by moving the foot down by a particle spacing +# points_matrix .-= (0.0, particle_spacing) +geometry = TrixiParticles.Polygon(points_matrix) + +point_in_geometry_algorithm = WindingNumberJacobson(; geometry, + winding_number_factor=0.4, + hierarchical_winding=true) + +# Returns `InitialCondition` +shape_sampled = ComplexShape(geometry; particle_spacing=fluid_particle_spacing, density=fluid_density, + point_in_geometry_algorithm) + +foot_sdf = SignedDistanceField(geometry, fluid_particle_spacing; + max_signed_distance=4 * fluid_particle_spacing, + use_for_boundary_packing=true) + +boundary_packing = sample_boundary(foot_sdf; boundary_density=fluid_density, + boundary_thickness=4 * fluid_particle_spacing) + +background_pressure = 1.0 +smoothing_length_packing = 0.8 * fluid_particle_spacing +foot_packing_system = ParticlePackingSystem(shape_sampled; smoothing_length=smoothing_length_packing, + signed_distance_field=foot_sdf, background_pressure) + +fluid_packing_system = ParticlePackingSystem(boundary_packing; smoothing_length=smoothing_length_packing, + signed_distance_field=foot_sdf, is_boundary=true, background_pressure, + boundary_compress_factor=0.8) + +min_corner = minimum(tank.fluid.coordinates, dims=2) .- fluid_particle_spacing / 2 +max_corner = maximum(tank.fluid.coordinates, dims=2) .+ fluid_particle_spacing / 2 +periodic_box = PeriodicBox(; min_corner, max_corner) +cell_list = FullGridCellList(; min_corner, max_corner) +neighborhood_search = GridNeighborhoodSearch{2}(; periodic_box, cell_list, update_strategy=ParallelUpdate()) + +semi_packing = Semidiscretization(foot_packing_system, fluid_packing_system; + neighborhood_search) + +ode_packing = semidiscretize(semi_packing, (0.0, 10.0)) + +sol_packing = solve(ode_packing, RDPK3SpFSAL35(); + save_everystep=false, + callback=CallbackSet(InfoCallback(interval=50), + # SolutionSavingCallback(interval=50, prefix="packing"), + UpdateCallback()), + dtmax=1e-2) + +hollow_sphere = InitialCondition(sol_packing, foot_packing_system, semi_packing) +fluid = setdiff(tank.fluid, hollow_sphere) + +fluid_packing_system = ParticlePackingSystem(fluid; smoothing_length=smoothing_length_packing, + signed_distance_field=nothing, background_pressure) + +boundary_packing_system = ParticlePackingSystem(hollow_sphere; smoothing_length=smoothing_length_packing, + fixed_system=true, signed_distance_field=nothing, background_pressure) + +semi_packing = Semidiscretization(fluid_packing_system, boundary_packing_system; + neighborhood_search) + +ode_packing = semidiscretize(semi_packing, (0.0, 2.0)) + +sol_packing = solve(ode_packing, RDPK3SpFSAL35(); + save_everystep=false, + callback=CallbackSet(InfoCallback(interval=50), + # SolutionSavingCallback(interval=50, prefix="packing"), + UpdateCallback()), + dtmax=1e-2) + +fluid = InitialCondition(sol_packing, fluid_packing_system, semi_packing) + +# ========================================================================================== +# ==== Fluid +smoothing_length = 2 * fluid_particle_spacing +smoothing_kernel = WendlandC2Kernel{2}() + +viscosity_fluid = ViscosityAdami(; nu) +# viscosity_fluid = ArtificialViscosityMonaghan(alpha=0.02, beta=0.0) +viscosity_wall = ViscosityAdami(; nu) +density_diffusion = DensityDiffusionMolteniColagrossi(delta=0.1) +fluid_density_calculator = ContinuityDensity() +fluid_system = WeaklyCompressibleSPHSystem(fluid, fluid_density_calculator, + state_equation, smoothing_kernel, + density_diffusion=density_diffusion, + pressure_acceleration=TrixiParticles.tensile_instability_control, + particle_shifting=TrixiParticles.ParticleShiftingSun2019(2.0), + smoothing_length, viscosity=viscosity_fluid) + +# ========================================================================================== +# ==== Boundary +# Movement function +frequency = 1.3 # Hz +amplitude = 0.18 # m +rotation_deg = 25 # degrees +rotation_phase_offset = 0.12 # periods +translation_vector = SVector(0.0, amplitude) +rotation_angle = rotation_deg * pi / 180 + +boundary_movement = TrixiParticles.oscillating_movement(frequency, + SVector(0.0, amplitude), + rotation_angle, center; + rotation_phase_offset, ramp_up=0.5) + +boundary_density_calculator = BernoulliPressureExtrapolation() +boundary_model = BoundaryModelDummyParticles(tank.boundary.density, tank.boundary.mass, + state_equation=state_equation, + boundary_density_calculator, + smoothing_kernel, smoothing_length) + +boundary_system = BoundarySPHSystem(tank.boundary, boundary_model) + +boundary_model_cylinder = BoundaryModelDummyParticles(hollow_sphere.density, + hollow_sphere.mass, + state_equation=state_equation, + boundary_density_calculator, + smoothing_kernel, smoothing_length, + viscosity=viscosity_wall) + +# boundary_system_cylinder = BoundarySPHSystem(hollow_sphere, boundary_model_cylinder, +# movement=boundary_movement) + +boundary_system_cylinder = TotalLagrangianSPHSystem(hollow_sphere, smoothing_kernel, smoothing_length, + 1e9, 0.3; + n_fixed_particles=nparticles(hollow_sphere), movement=boundary_movement, + boundary_model=boundary_model_cylinder, + penalty_force=PenaltyForceGanzenmueller(alpha=0.1)) + +# boundary_system_cylinder = TotalLagrangianSPHSystem(hollow_sphere, smoothing_kernel, smoothing_length, +# 1e5, 0.0; +# n_fixed_particles=nparticles(hollow_sphere), movement=boundary_movement, +# boundary_model=boundary_model) + +# ========================================================================================== +# ==== Simulation +min_corner = minimum(tank.fluid.coordinates, dims=2) .- fluid_particle_spacing / 2 +max_corner = maximum(tank.fluid.coordinates, dims=2) .+ fluid_particle_spacing / 2 +periodic_box = PeriodicBox(; min_corner, max_corner) +cell_list = FullGridCellList(; min_corner, max_corner) +neighborhood_search = GridNeighborhoodSearch{2}(; periodic_box, cell_list) + +semi = Semidiscretization(fluid_system, boundary_system_cylinder; + neighborhood_search) +ode = semidiscretize(semi, tspan) + +info_callback = InfoCallback(interval=10) +saving_callback = SolutionSavingCallback(dt=0.01, prefix="") +shifting_callback = ParticleShiftingCallback() + +stepsize_callback = StepsizeCallback(cfl=1.5) + +callbacks = CallbackSet(info_callback, saving_callback, shifting_callback, + stepsize_callback) + +# Use a Runge-Kutta method with automatic (error based) time step size control. +# Limiting of the maximum stepsize is necessary to prevent crashing. +# When particles are approaching a wall in a uniform way, they can be advanced +# with large time steps. Close to the wall, the stepsize has to be reduced drastically. +# Sometimes, the method fails to do so because forces become extremely large when +# fluid particles are very close to boundary particles, and the time integration method +# interprets this as an instability. +# fluid_dt = 1e-3 +# sol = solve(ode, RDPK3SpFSAL49(), +# # abstol=1.0e-6, # Default abstol is 1e-6 (may need to be tuned to prevent boundary penetration) +# # reltol=1.0e-4, # Default reltol is 1e-3 (may need to be tuned to prevent boundary penetration) +# adaptive=false, dt=fluid_dt, +# save_everystep=false, callback=callbacks); + +time_step = 1.0 +sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), + dt=time_step, # This is overwritten by the stepsize callback + save_everystep=false, callback=callbacks); + +# plane = interpolate_plane([0.0, -0.25], [1.0, 0.75], 0.0025, semi, fluid_system, sol) diff --git a/examples/fluid/vortex_street_postprocess.jl b/examples/fluid/vortex_street_postprocess.jl new file mode 100644 index 0000000000..155d592907 --- /dev/null +++ b/examples/fluid/vortex_street_postprocess.jl @@ -0,0 +1,168 @@ +using TrixiParticles +using OrdinaryDiffEqLowStorageRK + +# ========================================================================================== +# ==== Resolution +const fluid_particle_spacing = 0.2 + +# Change spacing ratio to 3 and boundary layers to 1 when using Monaghan-Kajtar boundary model +boundary_layers = 4 +spacing_ratio = 1 + +# ========================================================================================== +# ==== Experiment Setup +# Boundary geometry and initial fluid particle positions +tank_size = (65.0, 20.0) +initial_fluid_size = tank_size + +Re = 200 +initial_velocity = (1.0, 0.0) +nu = 1 * 1 / Re + +strouhal_number = 0.198 * (1 - 19.7 / Re) +frequency = strouhal_number * initial_velocity[1] / 1 + +tspan = (0.0, 50.0) + +fluid_density = 1000.0 +sound_speed = 10initial_velocity[1] +state_equation = StateEquationCole(; sound_speed, reference_density=fluid_density, + exponent=7) + +tank = RectangularTank(fluid_particle_spacing, initial_fluid_size, tank_size, fluid_density, + n_layers=boundary_layers, spacing_ratio=spacing_ratio, + faces=(false, false, true, true), velocity=initial_velocity) + +const radius = 2.0 +const center = SVector(5.0, 10.0) +hollow_sphere = SphereShape(fluid_particle_spacing, radius, Tuple(center), fluid_density, + n_layers=4, sphere_type=RoundSphere()) + +filled_sphere = SphereShape(fluid_particle_spacing, radius, Tuple(center), fluid_density, + sphere_type=RoundSphere()) + +# n_particles = round(Int, 0.12 / fluid_particle_spacing) +# cylinder = RectangularShape(fluid_particle_spacing, (n_particles, n_particles), (0.2 - 1.0, 0.24 - 1.0), density=fluid_density) + +fluid = setdiff(tank.fluid, filled_sphere) + +# ========================================================================================== +# ==== Fluid +smoothing_length = 2 * fluid_particle_spacing +smoothing_kernel = WendlandC2Kernel{2}() + +viscosity = ViscosityAdami(; nu) +density_diffusion = DensityDiffusionMolteniColagrossi(delta=0.1) +fluid_density_calculator = ContinuityDensity() +fluid_system = WeaklyCompressibleSPHSystem(fluid, fluid_density_calculator, + state_equation, smoothing_kernel, + density_diffusion=density_diffusion, + pressure_acceleration=TrixiParticles.tensile_instability_control, + # transport_velocity=TransportVelocityAdami(50_000.0), + smoothing_length, viscosity=viscosity) + +# ========================================================================================== +# ==== Boundary +boundary_density_calculator = AdamiPressureExtrapolation() +boundary_model = BoundaryModelDummyParticles(tank.boundary.density, tank.boundary.mass, + state_equation=state_equation, + boundary_density_calculator, + smoothing_kernel, smoothing_length) + +boundary_system = BoundarySPHSystem(tank.boundary, boundary_model) + +boundary_model_cylinder = BoundaryModelDummyParticles(hollow_sphere.density, + hollow_sphere.mass, + state_equation=state_equation, + boundary_density_calculator, + smoothing_kernel, smoothing_length, + viscosity=viscosity) + +boundary_system_cylinder = BoundarySPHSystem(hollow_sphere, boundary_model_cylinder) + +# ========================================================================================== +# ==== Simulation +periodic_box = PeriodicBox(min_corner=[0.0, -1.0], max_corner=[65.0, 21.0]) +cell_list = FullGridCellList(min_corner=[0.0, -1.0], max_corner=[65.0, 21.0]) +neighborhood_search = GridNeighborhoodSearch{2}(; periodic_box, cell_list) + +semi = Semidiscretization(fluid_system, boundary_system, boundary_system_cylinder; + neighborhood_search) +ode = semidiscretize(semi, tspan) + +info_callback = InfoCallback(interval=10) +saving_callback = SolutionSavingCallback(dt=1.0, prefix="tvf_old") +shifting_callback = ParticleShiftingCallback() + +# ========================================================================================== +# ==== Postprocessing +circle = SphereShape(fluid_particle_spacing, (2 * radius + fluid_particle_spacing) / 2, + Tuple(center), fluid_density, n_layers=1, + sphere_type=RoundSphere()) + +# Points for pressure interpolation, located at the wall interface +const data_points = copy(circle.coordinates) + +calculate_lift_force(system, v_ode, u_ode, semi, t) = nothing +function calculate_lift_force(system::TrixiParticles.FluidSystem, v_ode, u_ode, semi, t) + force = zero(SVector{ndims(system), eltype(system)}) + + values = interpolate_points(data_points, semi, system, v_ode, u_ode; cut_off_bnd=false, + clip_negative_pressure=false) + pressure = Array(values.pressure) + + for i in axes(data_points, 2) + point = TrixiParticles.current_coords(data_points, system, i) + + # F = ∑ -p_i * A_i * n_i + force -= pressure[i] * fluid_particle_spacing .* + TrixiParticles.normalize(point - center) + end + + return 2 * force[2] / (fluid_density * 1^2 * 2 * radius) +end + +calculate_drag_force(system, v_ode, u_ode, semi, t) = nothing +function calculate_drag_force(system::TrixiParticles.FluidSystem, v_ode, u_ode, semi, t) + force = zero(SVector{ndims(system), eltype(system)}) + + values = interpolate_points(data_points, semi, system, v_ode, u_ode; cut_off_bnd=false, + clip_negative_pressure=false) + pressure = Array(values.pressure) + + for i in axes(data_points, 2) + point = TrixiParticles.current_coords(data_points, system, i) + + # F = ∑ -p_i * A_i * n_i + force -= pressure[i] * fluid_particle_spacing .* + TrixiParticles.normalize(point - center) + end + + return 2 * force[1] / (fluid_density * 1^2 * 2 * radius) +end + +pp_callback = PostprocessCallback(; dt=0.5, + f_l=calculate_lift_force, f_d=calculate_drag_force, + filename="resulting_force_pst", + write_csv=true, write_file_interval=10) + +callbacks = CallbackSet(info_callback, saving_callback, shifting_callback, + UpdateCallback(), pp_callback) + +# Use a Runge-Kutta method with automatic (error based) time step size control. +# Limiting of the maximum stepsize is necessary to prevent crashing. +# When particles are approaching a wall in a uniform way, they can be advanced +# with large time steps. Close to the wall, the stepsize has to be reduced drastically. +# Sometimes, the method fails to do so because forces become extremely large when +# fluid particles are very close to boundary particles, and the time integration method +# interprets this as an instability. +sol = solve(ode, RDPK3SpFSAL49(), + abstol=1.0e-6, # Default abstol is 1e-6 (may need to be tuned to prevent boundary penetration) + reltol=1.0e-4, # Default reltol is 1e-3 (may need to be tuned to prevent boundary penetration) + save_everystep=false, callback=callbacks); + +# sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), +# dt=1.0, # This is overwritten by the stepsize callback +# save_everystep=false, callback=callbacks); + +# plane = interpolate_plane([0.0, -0.25], [1.0, 0.75], 0.0025, semi, fluid_system, sol) diff --git a/src/schemes/structure/total_lagrangian_sph/rhs.jl b/src/schemes/structure/total_lagrangian_sph/rhs.jl index d3ee370237..232f011838 100644 --- a/src/schemes/structure/total_lagrangian_sph/rhs.jl +++ b/src/schemes/structure/total_lagrangian_sph/rhs.jl @@ -108,6 +108,8 @@ function interact!(dv, v_particle_system, u_particle_system, neighbor_system::AbstractFluidSystem, semi; integrate_tlsph=semi.integrate_tlsph[], eachparticle=each_integrated_particle(particle_system)) + (; boundary_model) = particle_system + # Skip interaction if TLSPH systems are integrated separately integrate_tlsph || return dv From f66079bbf78af77db44c5329403332c6d3f0171c Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Mon, 16 Jun 2025 09:26:52 +0200 Subject: [PATCH 094/134] Fix fin example --- examples/fsi/fin_2d.jl | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/examples/fsi/fin_2d.jl b/examples/fsi/fin_2d.jl index 200a8aa535..64ad02bb47 100644 --- a/examples/fsi/fin_2d.jl +++ b/examples/fsi/fin_2d.jl @@ -8,7 +8,6 @@ n_particles_y = 3 # ========================================================================================== # ==== Experiment Setup -gravity = 2.0 tspan = (0.0, 3.0) fin_length = 0.6 @@ -100,17 +99,23 @@ fixed_particles = shape_sampled.initial_condition # zcolor=shape_sampled.winding_numbers) # Beam and clamped particles -n_particles_per_dimension = (round(Int, fin_length / particle_spacing) + 1,# + n_particles_clamp_x, +length_clamp = round(Int, 0.15 / particle_spacing) * particle_spacing # m +n_particles_per_dimension = (round(Int, (fin_length + length_clamp) / particle_spacing) + 2,# + n_particles_clamp_x, n_particles_y) # Note that the `RectangularShape` puts the first particle half a particle spacing away # from the boundary, which is correct for fluids, but not for solids. # We therefore need to pass `tlsph=true`. beam = RectangularShape(particle_spacing, n_particles_per_dimension, - center, density=density, tlsph=true) + (center[1] - length_clamp, center[2]), density=density, tlsph=true) + +# Clamp the blade in one layer of particles by moving the foot down by a particle spacing +fixed_particles.coordinates .-= (0.0, particle_spacing) solid = union(beam, fixed_particles) +n_fixed_particles = nparticles(solid) - nparticles(beam) + # Movement function frequency = 1.3 # Hz amplitude = 0.18 # m @@ -137,10 +142,9 @@ spacing_ratio = 1 # tspan = (0.0, 2.0) fluid_density = 1000.0 -nu = 0.1 / fluid_density # viscosity parameter sound_speed = 20.0 state_equation = StateEquationCole(; sound_speed, reference_density=fluid_density, - exponent=7, background_pressure=10_000.0) + exponent=1) tank = RectangularTank(fluid_particle_spacing, initial_fluid_size, tank_size, fluid_density, n_layers=boundary_layers, spacing_ratio=spacing_ratio, @@ -155,7 +159,8 @@ smoothing_length = sqrt(2) * particle_spacing smoothing_kernel = WendlandC2Kernel{2}() boundary_density_calculator = AdamiPressureExtrapolation() -viscosity = ViscosityAdami(nu=1e-6) +viscosity_fluid = ViscosityAdami(nu=1e-4) +viscosity_fin = ViscosityAdami(nu=1e-4) # For the FSI we need the hydrodynamic masses and densities in the solid boundary model hydrodynamic_densites = fluid_density * ones(size(solid.density)) @@ -165,15 +170,15 @@ boundary_model_solid = BoundaryModelDummyParticles(hydrodynamic_densites, hydrodynamic_masses, state_equation=state_equation, boundary_density_calculator, - smoothing_kernel, smoothing_length, - viscosity=viscosity) -# k_solid = 0.1 + smoothing_kernel, 2 * fluid_particle_spacing, + viscosity=viscosity_fin) + +# k_solid = 1.0 # beta_solid = fluid_particle_spacing / particle_spacing # boundary_model_solid = BoundaryModelMonaghanKajtar(k_solid, beta_solid, # particle_spacing, # hydrodynamic_masses) -n_fixed_particles = nparticles(solid) - nparticles(beam) solid_system = TotalLagrangianSPHSystem(solid, smoothing_kernel, smoothing_length, modulus, poisson_ratio; n_fixed_particles, movement=boundary_movement, @@ -186,11 +191,12 @@ smoothing_length = 2 * fluid_particle_spacing smoothing_kernel = WendlandC2Kernel{2}() fluid_density_calculator = ContinuityDensity() -density_diffusion = DensityDiffusionMolteniColagrossi(delta=0.1) +# density_diffusion = DensityDiffusionMolteniColagrossi(delta=0.1) +density_diffusion = DensityDiffusionAntuono(fluid, delta=0.1) fluid_system = WeaklyCompressibleSPHSystem(fluid, fluid_density_calculator, state_equation, smoothing_kernel, - smoothing_length, viscosity=viscosity, + smoothing_length, viscosity=viscosity_fluid, density_diffusion=density_diffusion, pressure_acceleration=tensile_instability_control) # fluid_system = EntropicallyDampedSPHSystem(fluid, smoothing_kernel, smoothing_length, @@ -213,13 +219,13 @@ min_corner = minimum(tank.boundary.coordinates, dims=2) .- fluid_particle_spacin max_corner = maximum(tank.boundary.coordinates, dims=2) .+ fluid_particle_spacing / 2 periodic_box = PeriodicBox(; min_corner, max_corner) cell_list = FullGridCellList(; min_corner, max_corner) -neighborhood_search = GridNeighborhoodSearch{2}(; periodic_box) +neighborhood_search = GridNeighborhoodSearch{2}(; periodic_box, cell_list, update_strategy=ParallelUpdate()) semi = Semidiscretization(fluid_system, boundary_system, solid_system; neighborhood_search) ode = semidiscretize(semi, tspan) info_callback = InfoCallback(interval=100) -saving_callback = SolutionSavingCallback(dt=0.02, prefix="") +saving_callback = SolutionSavingCallback(dt=0.01, prefix="") split_dt = 2e-5 split_integration = SplitIntegrationCallback(RDPK3SpFSAL35(), adaptive=false, dt=split_dt, @@ -241,6 +247,7 @@ callbacks = CallbackSet(info_callback, saving_callback, ParticleShiftingCallback # dtmax=1e-2, # Limit stepsize to prevent crashing # save_everystep=false, callback=callbacks, maxiters=10^8); +dt_fluid = 1.25e-4 sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), - dt=1.0, # This is overwritten by the stepsize callback + dt=dt_fluid, # This is overwritten by the stepsize callback save_everystep=false, callback=callbacks, maxiters=10^8); From 271768d5bb8f577b5aba1fe90f886c3bab91a531 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Mon, 16 Jun 2025 10:03:41 +0200 Subject: [PATCH 095/134] Use Molteni-Colagrossi DD --- examples/fsi/fin_2d.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/fsi/fin_2d.jl b/examples/fsi/fin_2d.jl index 64ad02bb47..66144d29c6 100644 --- a/examples/fsi/fin_2d.jl +++ b/examples/fsi/fin_2d.jl @@ -191,8 +191,8 @@ smoothing_length = 2 * fluid_particle_spacing smoothing_kernel = WendlandC2Kernel{2}() fluid_density_calculator = ContinuityDensity() -# density_diffusion = DensityDiffusionMolteniColagrossi(delta=0.1) -density_diffusion = DensityDiffusionAntuono(fluid, delta=0.1) +density_diffusion = DensityDiffusionMolteniColagrossi(delta=0.1) +# density_diffusion = DensityDiffusionAntuono(fluid, delta=0.1) fluid_system = WeaklyCompressibleSPHSystem(fluid, fluid_density_calculator, state_equation, smoothing_kernel, From 4e3abefcda6fd77625faffba278b6d3b6125bc6f Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Mon, 16 Jun 2025 10:04:31 +0200 Subject: [PATCH 096/134] Use higher resolution to fix BC --- examples/fsi/fin_2d.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/fsi/fin_2d.jl b/examples/fsi/fin_2d.jl index 66144d29c6..b4900d601d 100644 --- a/examples/fsi/fin_2d.jl +++ b/examples/fsi/fin_2d.jl @@ -4,7 +4,7 @@ using OrdinaryDiffEqSymplecticRK # ========================================================================================== # ==== Resolution -n_particles_y = 3 +n_particles_y = 6 # ========================================================================================== # ==== Experiment Setup From 317a1c654f2d105242720f44a04d2300d4cc4d22 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Mon, 16 Jun 2025 10:07:23 +0200 Subject: [PATCH 097/134] Reorganize example file --- examples/fsi/fin_2d.jl | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/examples/fsi/fin_2d.jl b/examples/fsi/fin_2d.jl index b4900d601d..ac2499e5e6 100644 --- a/examples/fsi/fin_2d.jl +++ b/examples/fsi/fin_2d.jl @@ -32,6 +32,11 @@ initial_velocity = (1.0, 0.0) # The structure starts at the position of the first particle and ends # at the position of the last particle. particle_spacing = fin_thickness / (n_particles_y - 1) +fluid_particle_spacing = particle_spacing + +smoothing_length = sqrt(2) * particle_spacing +smoothing_length = 2 * fluid_particle_spacing +smoothing_kernel = WendlandC2Kernel{2}() # Add particle_spacing/2 to the clamp_radius to ensure that particles are also placed on the radius # fixed_particles = SphereShape(particle_spacing, clamp_radius + particle_spacing / 2, @@ -129,10 +134,6 @@ boundary_movement = TrixiParticles.oscillating_movement(frequency, rotation_angle, center; rotation_phase_offset, ramp_up=0.5) -# ========================================================================================== -# ==== Resolution -fluid_particle_spacing = particle_spacing - # Change spacing ratio to 3 and boundary layers to 1 when using Monaghan-Kajtar boundary model boundary_layers = 4 spacing_ratio = 1 @@ -153,11 +154,6 @@ fluid = setdiff(tank.fluid, solid) # ========================================================================================== # ==== Solid -# The kernel in the reference uses a differently scaled smoothing length, -# so this is equivalent to the smoothing length of `sqrt(2) * particle_spacing` used in the paper. -smoothing_length = sqrt(2) * particle_spacing -smoothing_kernel = WendlandC2Kernel{2}() - boundary_density_calculator = AdamiPressureExtrapolation() viscosity_fluid = ViscosityAdami(nu=1e-4) viscosity_fin = ViscosityAdami(nu=1e-4) @@ -170,7 +166,7 @@ boundary_model_solid = BoundaryModelDummyParticles(hydrodynamic_densites, hydrodynamic_masses, state_equation=state_equation, boundary_density_calculator, - smoothing_kernel, 2 * fluid_particle_spacing, + smoothing_kernel, smoothing_length_fluid, viscosity=viscosity_fin) # k_solid = 1.0 @@ -179,7 +175,7 @@ boundary_model_solid = BoundaryModelDummyParticles(hydrodynamic_densites, # particle_spacing, # hydrodynamic_masses) -solid_system = TotalLagrangianSPHSystem(solid, smoothing_kernel, smoothing_length, +solid_system = TotalLagrangianSPHSystem(solid, smoothing_kernel, smoothing_length_solid, modulus, poisson_ratio; n_fixed_particles, movement=boundary_movement, boundary_model=boundary_model_solid, @@ -187,16 +183,13 @@ solid_system = TotalLagrangianSPHSystem(solid, smoothing_kernel, smoothing_lengt # ========================================================================================== # ==== Fluid -smoothing_length = 2 * fluid_particle_spacing -smoothing_kernel = WendlandC2Kernel{2}() - fluid_density_calculator = ContinuityDensity() density_diffusion = DensityDiffusionMolteniColagrossi(delta=0.1) # density_diffusion = DensityDiffusionAntuono(fluid, delta=0.1) fluid_system = WeaklyCompressibleSPHSystem(fluid, fluid_density_calculator, state_equation, smoothing_kernel, - smoothing_length, viscosity=viscosity_fluid, + smoothing_length_fluid, viscosity=viscosity_fluid, density_diffusion=density_diffusion, pressure_acceleration=tensile_instability_control) # fluid_system = EntropicallyDampedSPHSystem(fluid, smoothing_kernel, smoothing_length, @@ -209,7 +202,7 @@ boundary_density_calculator = AdamiPressureExtrapolation() boundary_model = BoundaryModelDummyParticles(tank.boundary.density, tank.boundary.mass, state_equation=state_equation, boundary_density_calculator, - smoothing_kernel, smoothing_length) + smoothing_kernel, smoothing_length_fluid) boundary_system = BoundarySPHSystem(tank.boundary, boundary_model) From f433c8f2c34692e60d510c9c66b6ba2c269e7f4e Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Fri, 27 Jun 2025 11:25:51 +0200 Subject: [PATCH 098/134] Add packing --- examples/fsi/fin_2d.jl | 125 +++++++++++++++++++++++++++++------------ 1 file changed, 88 insertions(+), 37 deletions(-) diff --git a/examples/fsi/fin_2d.jl b/examples/fsi/fin_2d.jl index ac2499e5e6..79588cdf90 100644 --- a/examples/fsi/fin_2d.jl +++ b/examples/fsi/fin_2d.jl @@ -1,6 +1,6 @@ using TrixiParticles using OrdinaryDiffEqLowStorageRK -using OrdinaryDiffEqSymplecticRK +# using OrdinaryDiffEqSymplecticRK # ========================================================================================== # ==== Resolution @@ -34,8 +34,8 @@ initial_velocity = (1.0, 0.0) particle_spacing = fin_thickness / (n_particles_y - 1) fluid_particle_spacing = particle_spacing -smoothing_length = sqrt(2) * particle_spacing -smoothing_length = 2 * fluid_particle_spacing +smoothing_length_solid = sqrt(2) * particle_spacing +smoothing_length_fluid = 2 * fluid_particle_spacing smoothing_kernel = WendlandC2Kernel{2}() # Add particle_spacing/2 to the clamp_radius to ensure that particles are also placed on the radius @@ -78,6 +78,8 @@ points_matrix = reinterpret(reshape, Float64, points) scaling = 0.3 / maximum(points_matrix, dims=2)[1] points_matrix .*= scaling points_matrix .+= (-0.3, -points_matrix[2, 1]) .+ center .- (0.0, 1e-4) +# # Clamp the blade in one layer of particles by moving the foot down by a particle spacing +# points_matrix .-= (0.0, particle_spacing) geometry = TrixiParticles.Polygon(points_matrix) # trixi2vtk(geometry) @@ -88,20 +90,7 @@ point_in_geometry_algorithm = WindingNumberJacobson(; geometry, # Returns `InitialCondition` shape_sampled = ComplexShape(geometry; particle_spacing, density=density, - store_winding_number=true, grid_offset=center, - point_in_geometry_algorithm) - -fixed_particles = shape_sampled.initial_condition - -# trixi2vtk(shape_sampled.initial_condition) - -# coordinates = stack(shape_sampled.grid) -# trixi2vtk(shape_sampled.signed_distance_field) -# trixi2vtk(coordinates, w=shape_sampled.winding_numbers) - -# Plot the winding number field -# plot(InitialCondition(; coordinates, density=1.0, particle_spacing), -# zcolor=shape_sampled.winding_numbers) + grid_offset=center, point_in_geometry_algorithm) # Beam and clamped particles length_clamp = round(Int, 0.15 / particle_spacing) * particle_spacing # m @@ -114,13 +103,86 @@ n_particles_per_dimension = (round(Int, (fin_length + length_clamp) / particle_s beam = RectangularShape(particle_spacing, n_particles_per_dimension, (center[1] - length_clamp, center[2]), density=density, tlsph=true) -# Clamp the blade in one layer of particles by moving the foot down by a particle spacing -fixed_particles.coordinates .-= (0.0, particle_spacing) +fixed_particles = setdiff(shape_sampled, beam) + +# solid = union(beam, fixed_particles) + +# Change spacing ratio to 3 and boundary layers to 1 when using Monaghan-Kajtar boundary model +boundary_layers = 4 +spacing_ratio = 1 +fluid_density = 1000.0 +tank = RectangularTank(fluid_particle_spacing, initial_fluid_size, tank_size, fluid_density, + n_layers=boundary_layers, spacing_ratio=spacing_ratio, + faces=(false, false, true, true), velocity=initial_velocity) +# fluid = setdiff(tank.fluid, solid) + +# ========================================================================================== +# ==== Packing +foot_sdf = SignedDistanceField(geometry, particle_spacing; + max_signed_distance=4 * particle_spacing, + use_for_boundary_packing=true) + +boundary_packing = sample_boundary(foot_sdf; boundary_density=density, + boundary_thickness=4 * particle_spacing) +boundary_packing = setdiff(boundary_packing, beam) + +background_pressure = 1.0 +smoothing_length_packing = 0.8 * particle_spacing +foot_packing_system = ParticlePackingSystem(fixed_particles; smoothing_length=smoothing_length_packing, + signed_distance_field=foot_sdf, background_pressure) + +fluid_packing_system = ParticlePackingSystem(boundary_packing; smoothing_length=smoothing_length_packing, + signed_distance_field=foot_sdf, is_boundary=true, background_pressure, + boundary_compress_factor=0.8) + +blade_packing_system = ParticlePackingSystem(beam; smoothing_length=smoothing_length_packing, + fixed_system=true, signed_distance_field=nothing, background_pressure) + +min_corner = minimum(tank.boundary.coordinates, dims=2) .- fluid_particle_spacing / 2 +max_corner = maximum(tank.boundary.coordinates, dims=2) .+ fluid_particle_spacing / 2 +periodic_box = PeriodicBox(; min_corner, max_corner) +cell_list = FullGridCellList(; min_corner, max_corner) +neighborhood_search = GridNeighborhoodSearch{2}(; periodic_box, cell_list, update_strategy=ParallelUpdate()) + +semi_packing = Semidiscretization(foot_packing_system, fluid_packing_system, + blade_packing_system; neighborhood_search) + +ode_packing = semidiscretize(semi_packing, (0.0, 10.0)) -solid = union(beam, fixed_particles) +sol_packing = solve(ode_packing, RDPK3SpFSAL35(); + save_everystep=false, + callback=CallbackSet(InfoCallback(interval=50), + # SolutionSavingCallback(interval=50, prefix="packing"), + UpdateCallback()), + dtmax=1e-2) + +packed_foot = InitialCondition(sol_packing, foot_packing_system, semi_packing) +solid = union(beam, packed_foot) +fluid = setdiff(tank.fluid, solid) n_fixed_particles = nparticles(solid) - nparticles(beam) +fluid_packing_system = ParticlePackingSystem(fluid; smoothing_length=smoothing_length_packing, + signed_distance_field=nothing, background_pressure) + +packing_boundary = union(solid, tank.boundary) +boundary_packing_system = ParticlePackingSystem(packing_boundary; smoothing_length=smoothing_length_packing, + fixed_system=true, signed_distance_field=nothing, background_pressure) + +semi_packing = Semidiscretization(fluid_packing_system, boundary_packing_system; + neighborhood_search) + +ode_packing = semidiscretize(semi_packing, (0.0, 5.0)) + +sol_packing = solve(ode_packing, RDPK3SpFSAL35(); + save_everystep=false, + callback=CallbackSet(InfoCallback(interval=50), + # SolutionSavingCallback(interval=50, prefix="packing"), + UpdateCallback()), + dtmax=1e-2) + +fluid = InitialCondition(sol_packing, fluid_packing_system, semi_packing) + # Movement function frequency = 1.3 # Hz amplitude = 0.18 # m @@ -134,23 +196,9 @@ boundary_movement = TrixiParticles.oscillating_movement(frequency, rotation_angle, center; rotation_phase_offset, ramp_up=0.5) -# Change spacing ratio to 3 and boundary layers to 1 when using Monaghan-Kajtar boundary model -boundary_layers = 4 -spacing_ratio = 1 - -# ========================================================================================== -# ==== Experiment Setup -# tspan = (0.0, 2.0) - -fluid_density = 1000.0 sound_speed = 20.0 state_equation = StateEquationCole(; sound_speed, reference_density=fluid_density, - exponent=1) - -tank = RectangularTank(fluid_particle_spacing, initial_fluid_size, tank_size, fluid_density, - n_layers=boundary_layers, spacing_ratio=spacing_ratio, - faces=(false, false, true, true), velocity=initial_velocity) -fluid = setdiff(tank.fluid, solid) + exponent=1, background_pressure=0.0) # ========================================================================================== # ==== Solid @@ -191,6 +239,7 @@ fluid_system = WeaklyCompressibleSPHSystem(fluid, fluid_density_calculator, state_equation, smoothing_kernel, smoothing_length_fluid, viscosity=viscosity_fluid, density_diffusion=density_diffusion, + particle_shifting=TrixiParticles.ParticleShiftingSun2019(0.1 * sound_speed), pressure_acceleration=tensile_instability_control) # fluid_system = EntropicallyDampedSPHSystem(fluid, smoothing_kernel, smoothing_length, # sound_speed, viscosity=ViscosityAdami(; nu), @@ -214,7 +263,8 @@ periodic_box = PeriodicBox(; min_corner, max_corner) cell_list = FullGridCellList(; min_corner, max_corner) neighborhood_search = GridNeighborhoodSearch{2}(; periodic_box, cell_list, update_strategy=ParallelUpdate()) -semi = Semidiscretization(fluid_system, boundary_system, solid_system; neighborhood_search) +semi = Semidiscretization(fluid_system, boundary_system, solid_system; neighborhood_search, + parallelization_backend=PolyesterBackend()) ode = semidiscretize(semi, tspan) info_callback = InfoCallback(interval=100) @@ -224,7 +274,8 @@ split_dt = 2e-5 split_integration = SplitIntegrationCallback(RDPK3SpFSAL35(), adaptive=false, dt=split_dt, maxiters=10^8) stepsize_callback = StepsizeCallback(cfl=0.5) -callbacks = CallbackSet(info_callback, saving_callback, ParticleShiftingCallback(), +shifting_callback = ParticleShiftingCallback() +callbacks = CallbackSet(info_callback, saving_callback, shifting_callback, split_integration, stepsize_callback) # Use a Runge-Kutta method with automatic (error based) time step size control. From d818e26fcd4f0ccf881e8cbb83884e1134f169cf Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Tue, 1 Jul 2025 12:34:16 +0200 Subject: [PATCH 099/134] Add fin DXF and packing window --- examples/fsi/fin_2d.jl | 84 +- examples/preprocessing/data/fin.dxf | 21776 ++++++++++++++++++++++++++ 2 files changed, 21810 insertions(+), 50 deletions(-) create mode 100644 examples/preprocessing/data/fin.dxf diff --git a/examples/fsi/fin_2d.jl b/examples/fsi/fin_2d.jl index 79588cdf90..efc1c7cd3e 100644 --- a/examples/fsi/fin_2d.jl +++ b/examples/fsi/fin_2d.jl @@ -38,49 +38,8 @@ smoothing_length_solid = sqrt(2) * particle_spacing smoothing_length_fluid = 2 * fluid_particle_spacing smoothing_kernel = WendlandC2Kernel{2}() -# Add particle_spacing/2 to the clamp_radius to ensure that particles are also placed on the radius -# fixed_particles = SphereShape(particle_spacing, clamp_radius + particle_spacing / 2, -# (center[1] - clamp_radius - particle_spacing, center[2] + fin_thickness / 2), density, -# # cutout_min=(center[1], center[2] - fin_thickness / 2), -# # cutout_max=center .+ (clamp_radius, fin_thickness / 2), -# tlsph=true) - -# fixed_beam = RectangularShape(particle_spacing, (round(Int, 0.05 / particle_spacing) + 1,# + n_particles_clamp_x, -# n_particles_y), -# center, density=density, tlsph=true) - -# fixed_particles = union(fixed_particles, fixed_beam) - -# n_particles_clamp_x = round(Int, clamp_radius / particle_spacing) - -using TrixiParticles -using PythonCall -using CondaPkg - -CondaPkg.add("svgpathtools") -CondaPkg.add("ezdxf") -svgpath = pyimport("svgpathtools") - -svg_path = "M509.299 100.016C507.966 91.5871 505.915 85.7111 503.145 82.3879C498.991 77.4031 479.883 60.7871 475.521 58.2947C471.16 55.8023 455.167 49.1559 448.936 47.702C442.705 46.2481 355.471 30.463 339.893 28.8014C329.508 27.6937 287.761 23.5397 214.651 16.3394C199.727 15.7768 185.488 15.1656 171.934 14.5056C151.602 13.5157 106.318 5.19544 82.4982 0.830064C58.6781 -3.53532 3.26262 9.37214 0.279574 38.2664C-2.54326 65.6089 16.5085 89.4186 34.9345 99.2843C49.9801 107.34 58.7166 107.403 71.5338 114.275C101.912 130.564 100.849 169.8 123.353 175.939C145.856 182.078 146.637 180.9 155.986 179.279C162.22 178.199 199.267 168.32 267.129 149.644L359.355 117.398L405.801 102.863C446.849 101.008 472.412 100.061 482.489 100.022C497.605 99.9635 507.524 100.062 509.299 100.016Z" - -path = svgpath.parse_path(svg_path) -t_range = range(0, 1, length=50 * length(path)) -points = [(pyconvert(Float64, p.real), -pyconvert(Float64, p.imag)) - for p in (path.point(t) for t in t_range)] - -# ezdxf = pyimport("ezdxf") -# doc = ezdxf.new(dxfversion="R2010") -# msp = doc.modelspace() -# msp.add_polyline2d(points) -# doc.saveas("output.dxf") - -points_matrix = reinterpret(reshape, Float64, points) -scaling = 0.3 / maximum(points_matrix, dims=2)[1] -points_matrix .*= scaling -points_matrix .+= (-0.3, -points_matrix[2, 1]) .+ center .- (0.0, 1e-4) -# # Clamp the blade in one layer of particles by moving the foot down by a particle spacing -# points_matrix .-= (0.0, particle_spacing) -geometry = TrixiParticles.Polygon(points_matrix) +file = joinpath(examples_dir(), "preprocessing", "data", "fin.dxf") +geometry = load_geometry(file) # trixi2vtk(geometry) @@ -101,7 +60,7 @@ n_particles_per_dimension = (round(Int, (fin_length + length_clamp) / particle_s # from the boundary, which is correct for fluids, but not for solids. # We therefore need to pass `tlsph=true`. beam = RectangularShape(particle_spacing, n_particles_per_dimension, - (center[1] - length_clamp, center[2]), density=density, tlsph=true) + (-length_clamp, 0.0), density=density, tlsph=true) fixed_particles = setdiff(shape_sampled, beam) @@ -157,22 +116,46 @@ sol_packing = solve(ode_packing, RDPK3SpFSAL35(); dtmax=1e-2) packed_foot = InitialCondition(sol_packing, foot_packing_system, semi_packing) + +# Move the fin to the center of the tank +packed_foot.coordinates .+= center +beam.coordinates .+= center + solid = union(beam, packed_foot) fluid = setdiff(tank.fluid, solid) n_fixed_particles = nparticles(solid) - nparticles(beam) -fluid_packing_system = ParticlePackingSystem(fluid; smoothing_length=smoothing_length_packing, +# Pack the fluid against the fin and the tank boundary +pack_window = TrixiParticles.Polygon(stack([ + [0.15, 0.42], + [0.3, 0.42], + [0.44, 0.48], + [1.12, 0.48], + [1.12, 0.52], + [0.55, 0.52], + [0.5, 0.56], + [0.24, 0.6], + [0.15, 0.6], + [0.15, 0.42] + ])) + +# Then, we extract the particles that fall inside this window +pack_fluid = intersect(fluid, pack_window) +# and those outside the window +fixed_fluid = setdiff(fluid, pack_fluid) +fixed_union = union(fixed_fluid, solid) + +fluid_packing_system = ParticlePackingSystem(pack_fluid; smoothing_length=smoothing_length_packing, signed_distance_field=nothing, background_pressure) -packing_boundary = union(solid, tank.boundary) -boundary_packing_system = ParticlePackingSystem(packing_boundary; smoothing_length=smoothing_length_packing, - fixed_system=true, signed_distance_field=nothing, background_pressure) +fixed_packing_system = ParticlePackingSystem(fixed_union; smoothing_length=smoothing_length_packing, + fixed_system=true, signed_distance_field=nothing, background_pressure) -semi_packing = Semidiscretization(fluid_packing_system, boundary_packing_system; +semi_packing = Semidiscretization(fluid_packing_system, fixed_packing_system; neighborhood_search) -ode_packing = semidiscretize(semi_packing, (0.0, 5.0)) +ode_packing = semidiscretize(semi_packing, (0.0, 2.0)) sol_packing = solve(ode_packing, RDPK3SpFSAL35(); save_everystep=false, @@ -182,6 +165,7 @@ sol_packing = solve(ode_packing, RDPK3SpFSAL35(); dtmax=1e-2) fluid = InitialCondition(sol_packing, fluid_packing_system, semi_packing) +fluid = union(fluid, fixed_fluid) # Movement function frequency = 1.3 # Hz diff --git a/examples/preprocessing/data/fin.dxf b/examples/preprocessing/data/fin.dxf new file mode 100644 index 0000000000..b69ee51b49 --- /dev/null +++ b/examples/preprocessing/data/fin.dxf @@ -0,0 +1,21776 @@ + 0 +SECTION + 2 +HEADER + 9 +$ACADVER + 1 +AC1024 + 9 +$ACADMAINTVER + 70 +6 + 9 +$DWGCODEPAGE + 3 +ANSI_1252 + 9 +$LASTSAVEDBY + 1 +ezdxf + 9 +$INSBASE + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$EXTMIN + 10 +1e+20 + 20 +1e+20 + 30 +1e+20 + 9 +$EXTMAX + 10 +-1e+20 + 20 +-1e+20 + 30 +-1e+20 + 9 +$LIMMIN + 10 +0.0 + 20 +0.0 + 9 +$LIMMAX + 10 +420.0 + 20 +297.0 + 9 +$ORTHOMODE + 70 +0 + 9 +$REGENMODE + 70 +1 + 9 +$FILLMODE + 70 +1 + 9 +$QTEXTMODE + 70 +0 + 9 +$MIRRTEXT + 70 +1 + 9 +$LTSCALE + 40 +1.0 + 9 +$ATTMODE + 70 +1 + 9 +$TEXTSIZE + 40 +2.5 + 9 +$TRACEWID + 40 +1.0 + 9 +$TEXTSTYLE + 7 +Standard + 9 +$CLAYER + 8 +0 + 9 +$CELTYPE + 6 +ByLayer + 9 +$CECOLOR + 62 +256 + 9 +$CELTSCALE + 40 +1.0 + 9 +$DISPSILH + 70 +0 + 9 +$DIMSCALE + 40 +1.0 + 9 +$DIMASZ + 40 +2.5 + 9 +$DIMEXO + 40 +0.625 + 9 +$DIMDLI + 40 +3.75 + 9 +$DIMRND + 40 +0.0 + 9 +$DIMDLE + 40 +0.0 + 9 +$DIMEXE + 40 +1.25 + 9 +$DIMTP + 40 +0.0 + 9 +$DIMTM + 40 +0.0 + 9 +$DIMTXT + 40 +2.5 + 9 +$DIMCEN + 40 +2.5 + 9 +$DIMTSZ + 40 +0.0 + 9 +$DIMTOL + 70 +0 + 9 +$DIMLIM + 70 +0 + 9 +$DIMTIH + 70 +0 + 9 +$DIMTOH + 70 +0 + 9 +$DIMSE1 + 70 +0 + 9 +$DIMSE2 + 70 +0 + 9 +$DIMTAD + 70 +1 + 9 +$DIMZIN + 70 +8 + 9 +$DIMBLK + 1 + + 9 +$DIMASO + 70 +1 + 9 +$DIMSHO + 70 +1 + 9 +$DIMPOST + 1 + + 9 +$DIMAPOST + 1 + + 9 +$DIMALT + 70 +0 + 9 +$DIMALTD + 70 +3 + 9 +$DIMALTF + 40 +0.03937007874 + 9 +$DIMLFAC + 40 +1.0 + 9 +$DIMTOFL + 70 +1 + 9 +$DIMTVP + 40 +0.0 + 9 +$DIMTIX + 70 +0 + 9 +$DIMSOXD + 70 +0 + 9 +$DIMSAH + 70 +0 + 9 +$DIMBLK1 + 1 + + 9 +$DIMBLK2 + 1 + + 9 +$DIMSTYLE + 2 +ISO-25 + 9 +$DIMCLRD + 70 +0 + 9 +$DIMCLRE + 70 +0 + 9 +$DIMCLRT + 70 +0 + 9 +$DIMTFAC + 40 +1.0 + 9 +$DIMGAP + 40 +0.625 + 9 +$DIMJUST + 70 +0 + 9 +$DIMSD1 + 70 +0 + 9 +$DIMSD2 + 70 +0 + 9 +$DIMTOLJ + 70 +0 + 9 +$DIMTZIN + 70 +8 + 9 +$DIMALTZ + 70 +0 + 9 +$DIMALTTZ + 70 +0 + 9 +$DIMUPT + 70 +0 + 9 +$DIMDEC + 70 +2 + 9 +$DIMTDEC + 70 +2 + 9 +$DIMALTU + 70 +2 + 9 +$DIMALTTD + 70 +3 + 9 +$DIMTXSTY + 7 +Standard + 9 +$DIMAUNIT + 70 +0 + 9 +$DIMADEC + 70 +0 + 9 +$DIMALTRND + 40 +0.0 + 9 +$DIMAZIN + 70 +0 + 9 +$DIMDSEP + 70 +44 + 9 +$DIMATFIT + 70 +3 + 9 +$DIMFRAC + 70 +0 + 9 +$DIMLDRBLK + 1 + + 9 +$DIMLUNIT + 70 +2 + 9 +$DIMLWD + 70 +-2 + 9 +$DIMLWE + 70 +-2 + 9 +$DIMTMOVE + 70 +0 + 9 +$DIMFXL + 40 +1.0 + 9 +$DIMFXLON + 70 +0 + 9 +$DIMJOGANG + 40 +0.785398163397 + 9 +$DIMTFILL + 70 +0 + 9 +$DIMTFILLCLR + 70 +0 + 9 +$DIMARCSYM + 70 +0 + 9 +$DIMLTYPE + 6 + + 9 +$DIMLTEX1 + 6 + + 9 +$DIMLTEX2 + 6 + + 9 +$DIMTXTDIRECTION + 70 +0 + 9 +$LUNITS + 70 +2 + 9 +$LUPREC + 70 +4 + 9 +$SKETCHINC + 40 +1.0 + 9 +$FILLETRAD + 40 +10.0 + 9 +$AUNITS + 70 +0 + 9 +$AUPREC + 70 +2 + 9 +$MENU + 1 +. + 9 +$ELEVATION + 40 +0.0 + 9 +$PELEVATION + 40 +0.0 + 9 +$THICKNESS + 40 +0.0 + 9 +$LIMCHECK + 70 +0 + 9 +$CHAMFERA + 40 +0.0 + 9 +$CHAMFERB + 40 +0.0 + 9 +$CHAMFERC + 40 +0.0 + 9 +$CHAMFERD + 40 +0.0 + 9 +$SKPOLY + 70 +0 + 9 +$TDCREATE + 40 +2460857.532060185 + 9 +$TDUCREATE + 40 +2458532.153996898 + 9 +$TDUPDATE + 40 +2460857.532060185 + 9 +$TDUUPDATE + 40 +2458532.1544311 + 9 +$TDINDWG + 40 +0.0 + 9 +$TDUSRTIMER + 40 +0.0 + 9 +$USRTIMER + 70 +1 + 9 +$ANGBASE + 50 +0.0 + 9 +$ANGDIR + 70 +0 + 9 +$PDMODE + 70 +0 + 9 +$PDSIZE + 40 +0.0 + 9 +$PLINEWID + 40 +0.0 + 9 +$SPLFRAME + 70 +0 + 9 +$SPLINETYPE + 70 +6 + 9 +$SPLINESEGS + 70 +8 + 9 +$HANDSEED + 5 +386 + 9 +$SURFTAB1 + 70 +6 + 9 +$SURFTAB2 + 70 +6 + 9 +$SURFTYPE + 70 +6 + 9 +$SURFU + 70 +6 + 9 +$SURFV + 70 +6 + 9 +$UCSBASE + 2 + + 9 +$UCSNAME + 2 + + 9 +$UCSORG + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$UCSXDIR + 10 +1.0 + 20 +0.0 + 30 +0.0 + 9 +$UCSYDIR + 10 +0.0 + 20 +1.0 + 30 +0.0 + 9 +$UCSORTHOREF + 2 + + 9 +$UCSORTHOVIEW + 70 +0 + 9 +$UCSORGTOP + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$UCSORGBOTTOM + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$UCSORGLEFT + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$UCSORGRIGHT + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$UCSORGFRONT + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$UCSORGBACK + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSBASE + 2 + + 9 +$PUCSNAME + 2 + + 9 +$PUCSORG + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSXDIR + 10 +1.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSYDIR + 10 +0.0 + 20 +1.0 + 30 +0.0 + 9 +$PUCSORTHOREF + 2 + + 9 +$PUCSORTHOVIEW + 70 +0 + 9 +$PUCSORGTOP + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSORGBOTTOM + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSORGLEFT + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSORGRIGHT + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSORGFRONT + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PUCSORGBACK + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$USERI1 + 70 +0 + 9 +$USERI2 + 70 +0 + 9 +$USERI3 + 70 +0 + 9 +$USERI4 + 70 +0 + 9 +$USERI5 + 70 +0 + 9 +$USERR1 + 40 +0.0 + 9 +$USERR2 + 40 +0.0 + 9 +$USERR3 + 40 +0.0 + 9 +$USERR4 + 40 +0.0 + 9 +$USERR5 + 40 +0.0 + 9 +$WORLDVIEW + 70 +1 + 9 +$SHADEDGE + 70 +3 + 9 +$SHADEDIF + 70 +70 + 9 +$TILEMODE + 70 +1 + 9 +$MAXACTVP + 70 +64 + 9 +$PINSBASE + 10 +0.0 + 20 +0.0 + 30 +0.0 + 9 +$PLIMCHECK + 70 +0 + 9 +$PEXTMIN + 10 +1e+20 + 20 +1e+20 + 30 +1e+20 + 9 +$PEXTMAX + 10 +-1e+20 + 20 +-1e+20 + 30 +-1e+20 + 9 +$PLIMMIN + 10 +0.0 + 20 +0.0 + 9 +$PLIMMAX + 10 +420.0 + 20 +297.0 + 9 +$UNITMODE + 70 +0 + 9 +$VISRETAIN + 70 +1 + 9 +$PLINEGEN + 70 +0 + 9 +$PSLTSCALE + 70 +1 + 9 +$TREEDEPTH + 70 +3020 + 9 +$CMLSTYLE + 2 +Standard + 9 +$CMLJUST + 70 +0 + 9 +$CMLSCALE + 40 +20.0 + 9 +$PROXYGRAPHICS + 70 +1 + 9 +$MEASUREMENT + 70 +1 + 9 +$CELWEIGHT +370 +-1 + 9 +$ENDCAPS +280 +0 + 9 +$JOINSTYLE +280 +0 + 9 +$LWDISPLAY +290 +0 + 9 +$INSUNITS + 70 +6 + 9 +$HYPERLINKBASE + 1 + + 9 +$STYLESHEET + 1 + + 9 +$XEDIT +290 +1 + 9 +$CEPSNTYPE +380 +0 + 9 +$PSTYLEMODE +290 +1 + 9 +$FINGERPRINTGUID + 2 +{FAAE200F-94FE-46F6-80FD-E3F6FFB18616} + 9 +$VERSIONGUID + 2 +{604F0C71-16BC-45AB-9B96-D28D4B8C28BA} + 9 +$EXTNAMES +290 +1 + 9 +$PSVPSCALE + 40 +0.0 + 9 +$OLESTARTUP +290 +0 + 9 +$SORTENTS +280 +127 + 9 +$INDEXCTL +280 +0 + 9 +$HIDETEXT +280 +1 + 9 +$XCLIPFRAME +280 +1 + 9 +$HALOGAP +280 +0 + 9 +$OBSCOLOR + 70 +257 + 9 +$OBSLTYPE +280 +0 + 9 +$INTERSECTIONDISPLAY +280 +0 + 9 +$INTERSECTIONCOLOR + 70 +257 + 9 +$DIMASSOC +280 +2 + 9 +$PROJECTNAME + 1 + + 9 +$CAMERADISPLAY +290 +0 + 9 +$LENSLENGTH + 40 +50.0 + 9 +$CAMERAHEIGHT + 40 +0.0 + 9 +$STEPSPERSEC + 40 +24.0 + 9 +$STEPSIZE + 40 +100.0 + 9 +$3DDWFPREC + 40 +2.0 + 9 +$PSOLWIDTH + 40 +0.005 + 9 +$PSOLHEIGHT + 40 +0.08 + 9 +$LOFTANG1 + 40 +1.570796326795 + 9 +$LOFTANG2 + 40 +1.570796326795 + 9 +$LOFTMAG1 + 40 +0.0 + 9 +$LOFTMAG2 + 40 +0.0 + 9 +$LOFTPARAM + 70 +7 + 9 +$LOFTNORMALS +280 +1 + 9 +$LATITUDE + 40 +37.795 + 9 +$LONGITUDE + 40 +-122.394 + 9 +$NORTHDIRECTION + 40 +0.0 + 9 +$TIMEZONE + 70 +-8000 + 9 +$LIGHTGLYPHDISPLAY +280 +1 + 9 +$TILEMODELIGHTSYNCH +280 +1 + 9 +$CMATERIAL +347 +20 + 9 +$SOLIDHIST +280 +0 + 9 +$SHOWHIST +280 +1 + 9 +$DWFFRAME +280 +2 + 9 +$DGNFRAME +280 +2 + 9 +$REALWORLDSCALE +290 +1 + 9 +$INTERFERECOLOR + 62 +256 + 9 +$CSHADOW +280 +0 + 9 +$SHADOWPLANELOCATION + 40 +0.0 + 0 +ENDSEC + 0 +SECTION + 2 +CLASSES + 0 +CLASS + 1 +ACDBDICTIONARYWDFLT + 2 +AcDbDictionaryWithDefault + 3 +ObjectDBX Classes + 90 +0 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +SUN + 2 +AcDbSun + 3 +SCENEOE + 90 +1153 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +VISUALSTYLE + 2 +AcDbVisualStyle + 3 +ObjectDBX Classes + 90 +4095 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +MATERIAL + 2 +AcDbMaterial + 3 +ObjectDBX Classes + 90 +1153 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +SCALE + 2 +AcDbScale + 3 +ObjectDBX Classes + 90 +1153 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +TABLESTYLE + 2 +AcDbTableStyle + 3 +ObjectDBX Classes + 90 +4095 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +MLEADERSTYLE + 2 +AcDbMLeaderStyle + 3 +ACDB_MLEADERSTYLE_CLASS + 90 +4095 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +DICTIONARYVAR + 2 +AcDbDictionaryVar + 3 +ObjectDBX Classes + 90 +0 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +CELLSTYLEMAP + 2 +AcDbCellStyleMap + 3 +ObjectDBX Classes + 90 +1152 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +MENTALRAYRENDERSETTINGS + 2 +AcDbMentalRayRenderSettings + 3 +SCENEOE + 90 +1024 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +ACDBDETAILVIEWSTYLE + 2 +AcDbDetailViewStyle + 3 +ObjectDBX Classes + 90 +1025 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +ACDBSECTIONVIEWSTYLE + 2 +AcDbSectionViewStyle + 3 +ObjectDBX Classes + 90 +1025 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +RASTERVARIABLES + 2 +AcDbRasterVariables + 3 +ISM + 90 +0 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +LAYOUT + 2 +AcDbLayout + 3 +ObjectDBX Classes + 90 +0 + 91 +0 +280 +0 +281 +0 + 0 +CLASS + 1 +ACDBPLACEHOLDER + 2 +AcDbPlaceHolder + 3 +ObjectDBX Classes + 90 +0 + 91 +0 +280 +0 +281 +0 + 0 +ENDSEC + 0 +SECTION + 2 +TABLES + 0 +TABLE + 2 +VPORT + 5 +8 +330 +0 +100 +AcDbSymbolTable + 70 +1 + 0 +VPORT + 5 +23 +330 +8 +100 +AcDbSymbolTableRecord +100 +AcDbViewportTableRecord + 2 +*Active + 70 +0 + 10 +0.0 + 20 +0.0 + 11 +1.0 + 21 +1.0 + 12 +0.0 + 22 +0.0 + 13 +0.0 + 23 +0.0 + 14 +0.5 + 24 +0.5 + 15 +0.5 + 25 +0.5 + 16 +0.0 + 26 +0.0 + 36 +1.0 + 17 +0.0 + 27 +0.0 + 37 +0.0 + 40 +1000.0 + 41 +1.34 + 42 +50.0 + 43 +0.0 + 44 +0.0 + 50 +0.0 + 51 +0.0 + 71 +0 + 72 +1000 + 73 +1 + 74 +3 + 75 +0 + 76 +0 + 77 +0 + 78 +0 +281 +0 + 65 +0 +146 +0.0 + 0 +ENDTAB + 0 +TABLE + 2 +LTYPE + 5 +2 +330 +0 +100 +AcDbSymbolTable + 70 +3 + 0 +LTYPE + 5 +24 +330 +2 +100 +AcDbSymbolTableRecord +100 +AcDbLinetypeTableRecord + 2 +ByBlock + 70 +0 + 3 + + 72 +65 + 73 +0 + 40 +0.0 + 0 +LTYPE + 5 +25 +330 +2 +100 +AcDbSymbolTableRecord +100 +AcDbLinetypeTableRecord + 2 +ByLayer + 70 +0 + 3 + + 72 +65 + 73 +0 + 40 +0.0 + 0 +LTYPE + 5 +26 +330 +2 +100 +AcDbSymbolTableRecord +100 +AcDbLinetypeTableRecord + 2 +Continuous + 70 +0 + 3 + + 72 +65 + 73 +0 + 40 +0.0 + 0 +ENDTAB + 0 +TABLE + 2 +LAYER + 5 +1 +330 +0 +100 +AcDbSymbolTable + 70 +2 + 0 +LAYER + 5 +27 +330 +1 +100 +AcDbSymbolTableRecord +100 +AcDbLayerTableRecord + 2 +0 + 70 +0 + 62 +7 + 6 +Continuous +370 +-3 +390 +13 +347 +21 + 0 +LAYER + 5 +28 +330 +1 +100 +AcDbSymbolTableRecord +100 +AcDbLayerTableRecord + 2 +Defpoints + 70 +0 + 62 +7 + 6 +Continuous +290 +0 +370 +-3 +390 +13 +347 +21 + 0 +ENDTAB + 0 +TABLE + 2 +STYLE + 5 +5 +330 +0 +100 +AcDbSymbolTable + 70 +1 + 0 +STYLE + 5 +29 +330 +5 +100 +AcDbSymbolTableRecord +100 +AcDbTextStyleTableRecord + 2 +Standard + 70 +0 + 40 +0.0 + 41 +1.0 + 50 +0.0 + 71 +0 + 42 +2.5 + 3 +txt + 4 + + 0 +ENDTAB + 0 +TABLE + 2 +VIEW + 5 +7 +330 +0 +100 +AcDbSymbolTable + 70 +0 + 0 +ENDTAB + 0 +TABLE + 2 +UCS + 5 +6 +330 +0 +100 +AcDbSymbolTable + 70 +0 + 0 +ENDTAB + 0 +TABLE + 2 +APPID + 5 +3 +330 +0 +100 +AcDbSymbolTable + 70 +3 + 0 +APPID + 5 +2A +330 +3 +100 +AcDbSymbolTableRecord +100 +AcDbRegAppTableRecord + 2 +ACAD + 70 +0 + 0 +APPID + 5 +383 +330 +3 +100 +AcDbSymbolTableRecord +100 +AcDbRegAppTableRecord + 2 +HATCHBACKGROUNDCOLOR + 70 +0 + 0 +APPID + 5 +384 +330 +3 +100 +AcDbSymbolTableRecord +100 +AcDbRegAppTableRecord + 2 +EZDXF + 70 +0 + 0 +ENDTAB + 0 +TABLE + 2 +DIMSTYLE + 5 +4 +330 +0 +100 +AcDbSymbolTable + 70 +1 +100 +AcDbDimStyleTable + 0 +DIMSTYLE +105 +2B +330 +4 +100 +AcDbSymbolTableRecord +100 +AcDbDimStyleTableRecord + 2 +Standard + 70 +0 + 40 +1.0 + 41 +2.5 + 42 +0.625 + 43 +3.75 + 44 +1.25 + 45 +0.0 + 46 +0.0 + 47 +0.0 + 48 +0.0 + 49 +2.5 +140 +2.5 +141 +2.5 +142 +0.0 +143 +0.03937007874 +144 +1.0 +145 +0.0 +146 +1.0 +147 +0.625 +148 +0.0 + 69 +0 + 70 +0 + 71 +0 + 72 +0 + 73 +0 + 74 +0 + 75 +0 + 76 +0 + 77 +1 + 78 +8 + 79 +3 +170 +0 +171 +3 +172 +1 +173 +0 +174 +0 +175 +0 +176 +0 +177 +0 +178 +0 +179 +2 +271 +2 +272 +2 +273 +2 +274 +3 +275 +0 +276 +0 +277 +2 +278 +44 +279 +0 +280 +0 +281 +0 +282 +0 +283 +0 +284 +8 +285 +0 +286 +0 +288 +0 +289 +3 +290 +0 +371 +-2 +372 +-2 + 0 +ENDTAB + 0 +TABLE + 2 +BLOCK_RECORD + 5 +9 +330 +0 +100 +AcDbSymbolTable + 70 +2 + 0 +BLOCK_RECORD + 5 +17 +330 +9 +100 +AcDbSymbolTableRecord +100 +AcDbBlockTableRecord + 2 +*Model_Space +340 +1A + 70 +0 +280 +1 +281 +0 + 0 +BLOCK_RECORD + 5 +1B +330 +9 +100 +AcDbSymbolTableRecord +100 +AcDbBlockTableRecord + 2 +*Paper_Space +340 +1E + 70 +0 +280 +1 +281 +0 + 0 +ENDTAB + 0 +ENDSEC + 0 +SECTION + 2 +BLOCKS + 0 +BLOCK + 5 +18 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbBlockBegin + 2 +*Model_Space + 70 +0 + 10 +0.0 + 20 +0.0 + 30 +0.0 + 3 +*Model_Space + 1 + + 0 +ENDBLK + 5 +19 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbBlockEnd + 0 +BLOCK + 5 +1C +330 +1B +100 +AcDbEntity + 8 +0 +100 +AcDbBlockBegin + 2 +*Paper_Space + 70 +0 + 10 +0.0 + 20 +0.0 + 30 +0.0 + 3 +*Paper_Space + 1 + + 0 +ENDBLK + 5 +1D +330 +1B +100 +AcDbEntity + 8 +0 +100 +AcDbBlockEnd + 0 +ENDSEC + 0 +SECTION + 2 +ENTITIES + 0 +POLYLINE + 5 +2F +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDb2dPolyline + 66 +1 + 10 +0.0 + 20 +0.0 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +31 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +0.0 + 20 +-0.00010000000000000286 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +32 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.00017528351092088723 + 20 +0.0009440195898606021 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +33 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.00036359447194017624 + 20 +0.0019417239071807327 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +34 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.0005649341828909971 + 20 +0.0028931130819437045 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +35 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.0007793039436066462 + 20 +0.0037981872441328474 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +36 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.0010067050539201983 + 20 +0.004656946523731456 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +37 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.0012471388136648387 + 20 +0.005469391050722874 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +38 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.0015006065226738086 + 20 +0.006235520955090397 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +39 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.0017671094807802934 + 20 +0.006955336366817361 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +3A +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.0020466489878173677 + 20 +0.007628837415887074 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +3B +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.002339226343618328 + 20 +0.008256024232282853 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +3C +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.002644842848016249 + 20 +0.008836896945988028 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +3D +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.002963499800844427 + 20 +0.009371455686985886 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +3E +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.0032951985019359364 + 20 +0.009859700585259784 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +3F +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.003636567674683844 + 20 +0.010297617810168594 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +40 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.003944789489121747 + 20 +0.01065139405731566 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +41 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.004319217536481679 + 20 +0.01105617700305394 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +42 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.004754609892778094 + 20 +0.011507421034458724 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +43 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.005245724634025284 + 20 +0.01200058053860531 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +44 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.005787319836237814 + 20 +0.012531109902568963 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +45 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.006374153575429975 + 20 +0.013094463513424998 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +46 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.007000983927616222 + 20 +0.013686095758248695 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +47 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.007662568968811012 + 20 +0.014301461024115336 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +48 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.008353666775028745 + 20 +0.014936013698100215 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +49 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.009069035422283767 + 20 +0.015585208167278618 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +4A +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.009803432986590588 + 20 +0.01624449881872584 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +4B +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.01055161754396361 + 20 +0.016909340039517162 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +4C +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.011308347170417177 + 20 +0.01757518621672788 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +4D +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.012068379941965801 + 20 +0.018237491737433276 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +4E +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.012826473934623828 + 20 +0.018891710988708656 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +4F +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.013577387224405713 + 20 +0.019533298357629285 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +50 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.014315877887325856 + 20 +0.020157708231270464 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +51 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.01503670399939866 + 20 +0.02076039499670749 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +52 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.01573462363663858 + 20 +0.02133681304101563 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +53 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.016404394875059958 + 20 +0.021882416751270194 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +54 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.01704077579067731 + 20 +0.022392660514546456 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +55 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.01763852445950498 + 20 +0.022862998717919712 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +56 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.018192398957557365 + 20 +0.023288885748465257 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +57 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.018697157360848926 + 20 +0.02366577599325837 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +58 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.019147557745394117 + 20 +0.023989123839374342 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +59 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.01953835818720734 + 20 +0.024254383673888463 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +5A +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.01986431676230288 + 20 +0.02445700988387603 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +5B +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.020254357686325997 + 20 +0.024672500558981875 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +5C +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.02073676397497981 + 20 +0.024922074604494165 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +5D +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.021295655898442345 + 20 +0.025197768780830576 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +5E +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.02192309920388963 + 20 +0.025496116812679395 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +5F +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.022611159638497857 + 20 +0.02581365242472891 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +60 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.023351902949443004 + 20 +0.026146909341667413 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +61 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.02413739488390121 + 20 +0.02649242128818318 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +62 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.024959701189048444 + 20 +0.026846721988964505 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +63 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.025810887612060962 + 20 +0.027206345168699665 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +64 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.02668301990011468 + 20 +0.02756782455207695 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +65 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.027568163800385737 + 20 +0.027927693863784648 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +66 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.028458385060050218 + 20 +0.028282486828511044 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +67 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.02934574942628415 + 20 +0.02862873717094442 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +68 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.030222322646263677 + 20 +0.028962978615773072 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +69 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.031080170467164825 + 20 +0.02928174488768527 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +6A +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.03191135863616368 + 20 +0.029581569711369316 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +6B +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.03270795290043632 + 20 +0.029858986811513482 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +6C +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.03346201900715884 + 20 +0.03011052991280606 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +6D +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.03416562270350726 + 20 +0.030332732739935336 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +6E +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.03481082973665772 + 20 +0.0305221290175896 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +6F +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.03538970585378626 + 20 +0.030675252470457135 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +70 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.035658783132023364 + 20 +0.03073859404865303 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +71 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.035843204187391564 + 20 +0.030778802306374368 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +72 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.03606868051476314 + 20 +0.030826258436102018 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +73 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.036334228869552565 + 20 +0.03088077916708409 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +74 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.03663886600717414 + 20 +0.03094218122856872 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +75 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.03698160868304223 + 20 +0.03101028134980403 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +76 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.037361473652571364 + 20 +0.031084896260038145 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +77 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.037777477671175785 + 20 +0.031165842688519192 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +78 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.03822863749426986 + 20 +0.03125293736449529 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +79 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.038713969877267995 + 20 +0.03134599701721458 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +7A +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.03923249157558467 + 20 +0.03144483837592516 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +7B +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.039783219344634135 + 20 +0.031549278169875176 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +7C +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.0403651699398308 + 20 +0.03165913312831275 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +7D +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.04097736011658909 + 20 +0.031774219980486004 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +7E +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.04161880663032336 + 20 +0.031894355455643064 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +7F +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.04228852623644791 + 20 +0.032019356283032055 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +80 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.042985535690377275 + 20 +0.03214903919190111 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +81 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.043708851747525646 + 20 +0.032283220911498337 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +82 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.044457491163307605 + 20 +0.03242171817107187 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +83 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.0452304706931374 + 20 +0.03256434769986984 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +84 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.046026807092429456 + 20 +0.03271092622714036 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +85 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.04684551711659807 + 20 +0.03286127048213157 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +86 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.04768561752105771 + 20 +0.033015197194091586 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +87 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.0485461250612228 + 20 +0.03317252309226853 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +88 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.049426056492507586 + 20 +0.03333306490591054 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +89 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.05032442857032654 + 20 +0.033496639364265716 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +8A +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.051240258050093995 + 20 +0.03366306319658221 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +8B +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.05217256168722437 + 20 +0.033832153132108134 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +8C +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.053120356237132016 + 20 +0.03400372590009162 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +8D +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.05408265845523133 + 20 +0.03417759822978078 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +8E +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.055058485096936643 + 20 +0.03435358685042376 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +8F +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.0560468529176624 + 20 +0.03453150849126866 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +90 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.057046778672822956 + 20 +0.03471117988156362 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +91 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.05805727911783265 + 20 +0.03489241775055677 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +92 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.05907737100810595 + 20 +0.03507503882749623 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +93 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.060106071099057135 + 20 +0.035258859841630116 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +94 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.06114239614610065 + 20 +0.035443697522206566 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +95 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.062185362904650876 + 20 +0.035629368598473696 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +96 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.06323398813012213 + 20 +0.03581568979967963 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +97 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.06428728857792881 + 20 +0.036002477855072504 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +98 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.06534428100348538 + 20 +0.036189549493900436 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +99 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.06640398216220611 + 20 +0.036376721445411556 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +9A +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.06746540880950547 + 20 +0.036563810438853976 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +9B +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.06852757770079776 + 20 +0.03675063320347584 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +9C +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.0695895055914974 + 20 +0.036937006468525255 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +9D +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.07065020923701876 + 20 +0.03712274696325035 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +9E +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.07170870539277618 + 20 +0.037307671416899266 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +9F +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.07276401081418413 + 20 +0.037491596558720114 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +A0 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.07381514225665695 + 20 +0.037674339117961024 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +A1 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.07486111647560895 + 20 +0.03785571582387011 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +A2 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.0759009502264546 + 20 +0.03803554340569551 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +A3 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.07693366026460824 + 20 +0.03821363859268535 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +A4 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.07795826334548428 + 20 +0.038389818114087745 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +A5 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.07897377622449706 + 20 +0.03856389869915083 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +A6 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.07997921565706095 + 20 +0.038735697077122724 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +A7 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.08097359839859036 + 20 +0.03890502997725155 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +A8 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.08195594120449967 + 20 +0.039071714128785434 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +A9 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.08292526083020324 + 20 +0.039235566260972514 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +AA +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.08388057403111548 + 20 +0.0393964031030609 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +AB +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.08482089756265071 + 20 +0.039554041384298716 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +AC +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.08574524818022339 + 20 +0.03970829783393409 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +AD +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.0866526426392478 + 20 +0.039858989181215165 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +AE +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.08754209769513843 + 20 +0.04000593215539004 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +AF +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.0884126301033096 + 20 +0.040148943485706856 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +B0 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.08926325661917567 + 20 +0.040287839901413734 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +B1 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.09009299399815102 + 20 +0.040422438131758796 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +B2 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.09090085899565006 + 20 +0.04055255490599017 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +B3 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.0916858683670872 + 20 +0.04067800695335598 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +B4 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.09244703886787675 + 20 +0.040798611003104356 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +B5 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.09318338725343314 + 20 +0.040914183784483416 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +B6 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.0938939302791707 + 20 +0.04102454202674129 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +B7 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.09457768470050384 + 20 +0.0411295024591261 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +B8 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.09523366727284693 + 20 +0.041228881810885964 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +B9 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.09586089475161436 + 20 +0.041322496811269024 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +BA +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.0964583838922205 + 20 +0.0414101641895234 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +BB +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.09702515145007973 + 20 +0.04149170067489721 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +BC +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.09756021418060645 + 20 +0.04156692299663858 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +BD +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.09806258883921501 + 20 +0.04163564788399564 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +BE +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.09853129218131981 + 20 +0.04169769206621651 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +BF +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.0989653409623352 + 20 +0.04175287227254933 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +C0 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.09936375193767558 + 20 +0.0418010052322422 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +C1 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.09972554186275531 + 20 +0.04184190767454325 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +C2 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.09995304002710165 + 20 +0.0418661899502048 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +C3 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.10016700328101996 + 20 +0.04188885151517696 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +C4 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.10039375511735624 + 20 +0.0419127552788092 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +C5 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.10063329554049044 + 20 +0.0419379012411015 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +C6 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.10088562455480257 + 20 +0.04196428940205388 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +C7 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.10115074216467274 + 20 +0.04199191976166633 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +C8 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1014286483744809 + 20 +0.04202079231993884 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +C9 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.10171934318860718 + 20 +0.042050907076871424 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +CA +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.10202282661143153 + 20 +0.04208226403246408 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +CB +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.10233909864733406 + 20 +0.04211486318671681 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +CC +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.10266815930069476 + 20 +0.042148704539629614 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +CD +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.10301000857589368 + 20 +0.042183788091202476 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +CE +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.10336464647731086 + 20 +0.04222011384143542 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +CF +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.10373207300932633 + 20 +0.042257681790328426 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +D0 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.10411228817632015 + 20 +0.04229649193788151 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +D1 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1045052919826723 + 20 +0.042336544284094654 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +D2 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.10491108443276287 + 20 +0.04237783882896788 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +D3 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.10532966553097192 + 20 +0.04242037557250117 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +D4 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1057610352816794 + 20 +0.042464154514694534 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +D5 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1062051936892654 + 20 +0.04250917565554796 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +D6 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.10666214075810995 + 20 +0.042555438995061465 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +D7 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.10713187649259309 + 20 +0.04260294453323504 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +D8 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.10761440089709487 + 20 +0.04265169227006868 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +D9 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.10810971397599525 + 20 +0.0427016822055624 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +DA +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1086178157336744 + 20 +0.04275291433971619 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +DB +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.10913870617451224 + 20 +0.042805388672530045 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +DC +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.10967238530288886 + 20 +0.04285910520400396 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +DD +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1102188531231843 + 20 +0.042914063934137964 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +DE +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.11077810963977855 + 20 +0.042970264862932026 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +DF +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.11135015485705171 + 20 +0.04302770799038617 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +E0 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.11193498877938374 + 20 +0.043086393316500375 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +E1 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.11253261141115478 + 20 +0.04314632084127465 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +E2 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.11314302275674479 + 20 +0.043207490564709 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +E3 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.11376622282053381 + 20 +0.04326990248680342 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +E4 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1144022116069019 + 20 +0.043333556607557916 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +E5 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1150509891202291 + 20 +0.043398452926972475 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +E6 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.11571255536489541 + 20 +0.04346459144504711 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +E7 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1163869103452809 + 20 +0.043531972161781805 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +E8 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.11707405406576563 + 20 +0.04360059507717658 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +E9 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1177739865307296 + 20 +0.04367046019123142 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +EA +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1184867077445528 + 20 +0.04374156750394633 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +EB +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.11921221771161533 + 20 +0.04381391701532132 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +EC +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.11995051643629726 + 20 +0.04388750872535637 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +ED +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.12070160392297855 + 20 +0.04396234263405149 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +EE +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.12146548017603925 + 20 +0.044038418741406696 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +EF +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.12224214519985946 + 20 +0.04411573704742196 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +F0 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.12303159899881913 + 20 +0.04419429755209729 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +F1 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.12383384157729835 + 20 +0.0442741002554327 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +F2 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.12464887293967716 + 20 +0.04435514515742818 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +F3 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1254766930903356 + 20 +0.04443743225808373 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +F4 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.12631730203365363 + 20 +0.04452096155739934 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +F5 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.12717069977401138 + 20 +0.04460573305537503 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +F6 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.12803688631578886 + 20 +0.04469174675201079 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +F7 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.12891586166336602 + 20 +0.04477900264730662 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +F8 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.12980762582112307 + 20 +0.04486750074126252 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +F9 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.13071217879343994 + 20 +0.04495724103387849 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +FA +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1316295205846966 + 20 +0.04504822352515453 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +FB +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1325596511992732 + 20 +0.04514044821509064 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +FC +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1335025706415498 + 20 +0.04523391510368683 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +FD +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.13445827891590628 + 20 +0.04532862419094308 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +FE +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.13542677602672284 + 20 +0.0454245754768594 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +FF +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.13640806197837946 + 20 +0.045521768961435796 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +100 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.13740213677525612 + 20 +0.04562020464467226 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +101 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.13840900042173293 + 20 +0.04571988252656879 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +102 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.13942865292218992 + 20 +0.0458208026071254 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +103 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.14046109428100703 + 20 +0.04592296488634208 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +104 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.14150632450256445 + 20 +0.04602636936421882 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +105 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.14256434359124212 + 20 +0.04613101604075564 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +106 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.14363515155142006 + 20 +0.046236904915952524 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +107 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1447187483874784 + 20 +0.04634403598980948 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +108 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.14581513410379707 + 20 +0.046452409262326515 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +109 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.14692430870475612 + 20 +0.04656202473350361 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +10A +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1480462721947357 + 20 +0.046672882403340776 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +10B +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.14918102457811575 + 20 +0.04678498227183802 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +10C +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.15032856585927631 + 20 +0.04689832433899533 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +10D +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.15148889604259747 + 20 +0.047012908604812706 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +10E +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1526620151324592 + 20 +0.04712873506929016 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +10F +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.15384792313324158 + 20 +0.04724580373242769 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +110 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.15504662004932454 + 20 +0.04736411459422527 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +111 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.15625810588508832 + 20 +0.04748366765468294 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +112 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1574823806449128 + 20 +0.04760446291380067 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +113 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.15871944433317808 + 20 +0.04772650037157848 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +114 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.15996929695426418 + 20 +0.04784978002801635 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +115 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.16123193851255116 + 20 +0.0479743018831143 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +116 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.16250736901241894 + 20 +0.048100065936872306 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +117 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.16379558845824768 + 20 +0.04822707218929039 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +118 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.16509659685441744 + 20 +0.04835532064036855 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +119 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.16641039420530815 + 20 +0.04848481129010678 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +11A +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.16773698051529995 + 20 +0.04861554413850508 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +11B +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.16907635578877278 + 20 +0.04874751918556344 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +11C +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.17042852003010675 + 20 +0.048880736431281886 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +11D +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1717934732436818 + 20 +0.04901519587566039 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +11E +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1731712154338781 + 20 +0.04915089751869897 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +11F +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1741596943528995 + 20 +0.049211918564809734 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +120 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.174990619985511 + 20 +0.0492435630486022 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +121 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.17581912524139995 + 20 +0.04927537929387907 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +122 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.17664521012056636 + 20 +0.04930736732298474 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +123 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.17746887462301025 + 20 +0.04933952715826361 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +124 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.17829011874873155 + 20 +0.049371858822060084 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +125 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.17910894249773035 + 20 +0.04940436233671857 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +126 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1799253458700066 + 20 +0.049437037724583466 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +127 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.18073932886556032 + 20 +0.049469885007999176 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +128 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.18155089148439152 + 20 +0.0495029042093101 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +129 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.18236003372650017 + 20 +0.04953609535086065 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +12A +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.18316675559188622 + 20 +0.049569458454995205 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +12B +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.18397105708054978 + 20 +0.0496029935440582 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +12C +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.18477293819249083 + 20 +0.049636700640394014 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +12D +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.18557239892770933 + 20 +0.04967057976634706 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +12E +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.18636943928620528 + 20 +0.04970463094426174 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +12F +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1871640592679787 + 20 +0.04973885419648245 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +130 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.18795625887302958 + 20 +0.0497732495453536 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +131 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.18874603810135787 + 20 +0.04980781701321958 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +132 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.18953339695296367 + 20 +0.049842556622424816 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +133 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.19031833542784693 + 20 +0.04987746839531369 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +134 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.19110085352600767 + 20 +0.049912552354230616 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +135 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.19188095124744584 + 20 +0.04994780852152 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +136 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.19265862859216148 + 20 +0.049983236919526226 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +137 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.19343388556015456 + 20 +0.05001883757059371 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +138 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.19420672215142512 + 20 +0.050054610497066845 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +139 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.19497713836597316 + 20 +0.05009055572129005 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +13A +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.19574513420379863 + 20 +0.05012667326560772 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +13B +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.19651070966490158 + 20 +0.05016296315236426 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +13C +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.19727386474928202 + 20 +0.05019942540390406 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +13D +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1980345994569399 + 20 +0.050236060042571536 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +13E +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.19877267146877997 + 20 +0.05027190026431345 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +13F +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.19932081065879115 + 20 +0.05030141913164245 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +140 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1998880238290493 + 20 +0.05033657175251838 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +141 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.20047376447210924 + 20 +0.05037722525371138 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +142 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.201077486080526 + 20 +0.05042324676199161 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +143 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.20169864214685454 + 20 +0.050474503404129185 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +144 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.20233668616364978 + 20 +0.05053086230689427 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +145 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.20299107162346663 + 20 +0.050592190597056996 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +146 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.20366125201886015 + 20 +0.05065835540138751 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +147 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.20434668084238522 + 20 +0.05072922384665595 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +148 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.20504681158659682 + 20 +0.050804663059632466 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +149 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.20576109774404983 + 20 +0.0508845401670872 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +14A +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.20648899280729932 + 20 +0.050968722295790296 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +14B +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.20722995026890012 + 20 +0.051057076572511885 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +14C +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.20798342362140726 + 20 +0.05114947012402212 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +14D +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2087488663573757 + 20 +0.05124577007709115 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +14E +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.20952573196936033 + 20 +0.051345843558489104 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +14F +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.21031347394991617 + 20 +0.05144955769498613 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +150 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.21111154579159813 + 20 +0.05155677961335237 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +151 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.21191940098696116 + 20 +0.05166737644035797 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +152 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.21273649302856018 + 20 +0.05178121530277307 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +153 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2135622754089502 + 20 +0.05189816332736782 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +154 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.21439620162068618 + 20 +0.052018087640912355 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +155 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.21523772515632303 + 20 +0.05214085537017682 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +156 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.21608629950841574 + 20 +0.05226633364193135 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +157 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2169413781695192 + 20 +0.05239438958294611 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +158 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2178024146321884 + 20 +0.05252489031999122 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +159 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.21866886238897826 + 20 +0.05265770297983682 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +15A +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.21954017493244382 + 20 +0.05279269468925308 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +15B +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.22041580575513997 + 20 +0.05292973257501012 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +15C +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.22129520834962163 + 20 +0.05306868376387809 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +15D +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2221778362084438 + 20 +0.053209415382627134 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +15E +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.22306314282416143 + 20 +0.053351794558027396 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +15F +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2239505816893294 + 20 +0.053495688416849006 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +160 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2248396062965028 + 20 +0.05364096408586212 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +161 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.22572967013823647 + 20 +0.05378748869183689 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +162 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.22662022670708537 + 20 +0.05393512936154343 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +163 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2275107294956045 + 20 +0.054083753221751914 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +164 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2284006319963488 + 20 +0.05423322739923246 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +165 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.22928938770187318 + 20 +0.054383419020755226 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +166 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2301764501047326 + 20 +0.054534195213090345 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +167 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.23106127269748206 + 20 +0.05468542310300797 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +168 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.23194330897267648 + 20 +0.05483696981727823 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +169 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.23282201242287082 + 20 +0.054988702482671285 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +16A +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.23369683654062 + 20 +0.05514048822595727 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +16B +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.23456723481847902 + 20 +0.05529219417390632 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +16C +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.23543266074900282 + 20 +0.05544368745328859 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +16D +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.23629256782474628 + 20 +0.055594835190874214 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +16E +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.23714640953826444 + 20 +0.05574550451343334 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +16F +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.23799363938211224 + 20 +0.05589556254773611 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +170 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2388337108488446 + 20 +0.05604487642055266 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +171 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2396660774310165 + 20 +0.05619331325865315 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +172 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.24049019262118287 + 20 +0.056340740188807706 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +173 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.24130550991189864 + 20 +0.05648702433778647 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +174 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2421114827957188 + 20 +0.0566320328323596 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +175 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2429075647651983 + 20 +0.05677563279929723 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +176 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2436932093128921 + 20 +0.0569176913653695 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +177 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.24446786993135514 + 20 +0.05705807565734656 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +178 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.24523100011314236 + 20 +0.057196652801998545 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +179 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2459820533508087 + 20 +0.0573332899260956 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +17A +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.24672048313690909 + 20 +0.057467854156407865 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +17B +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.24744574296399857 + 20 +0.05760021261970549 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +17C +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.24815728632463205 + 20 +0.05773023244275863 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +17D +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.24885456671136444 + 20 +0.0578577807523374 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +17E +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.24953703761675075 + 20 +0.057982724675211955 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +17F +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2502041525333459 + 20 +0.05810493133815244 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +180 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2508553649537048 + 20 +0.058224267867928996 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +181 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.25148412627767264 + 20 +0.058339357657130234 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +182 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.25207940009355373 + 20 +0.05843858306040156 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +183 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2526951223608269 + 20 +0.058526155972813854 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +184 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.253330508534902 + 20 +0.058602088401815584 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +185 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2539847740711891 + 20 +0.058666392354855226 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +186 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.254657134425098 + 20 +0.058719079839381266 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +187 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.25534680505203877 + 20 +0.05876016286284218 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +188 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2560530014074214 + 20 +0.05878965343268644 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +189 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2567749389466557 + 20 +0.05880756355636253 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +18A +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.25751183312515175 + 20 +0.05881390524131893 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +18B +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2582628993983195 + 20 +0.058808690495004104 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +18C +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.25902735322156883 + 20 +0.05879193132486654 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +18D +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.25980441005030974 + 20 +0.05876363973835472 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +18E +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2605932853399522 + 20 +0.058723827742917116 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +18F +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.26139319454590615 + 20 +0.058672507346002205 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +190 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2622033531235815 + 20 +0.058609690555058463 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +191 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.26302297652838835 + 20 +0.05853538937753437 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +192 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2638512802157365 + 20 +0.0584496158208784 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +193 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.26468747964103595 + 20 +0.058352381892539044 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +194 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.26553079025969667 + 20 +0.058243699599964775 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +195 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.26638042752712865 + 20 +0.05812358095060406 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +196 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2672356068987418 + 20 +0.057992037951905384 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +197 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2680955438299461 + 20 +0.05784908261131722 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +198 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2689594537761515 + 20 +0.057694726936288056 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +199 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2698265521927679 + 20 +0.05752898293426636 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +19A +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.27069605453520534 + 20 +0.05735186261270061 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +19B +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2715671762588737 + 20 +0.057163377979039295 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +19C +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.272439132819183 + 20 +0.05696354104073088 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +19D +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2733111396715432 + 20 +0.05675236380522385 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +19E +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.27418241227136425 + 20 +0.05652985827996668 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +19F +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.275052166074056 + 20 +0.05629603647240784 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1A0 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2759196165350286 + 20 +0.05605091038999582 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1A1 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2767839791096918 + 20 +0.055794492040179104 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1A2 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2776444692534557 + 20 +0.05552679343040616 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1A3 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2785003024217302 + 20 +0.055247826568125456 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1A4 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2793506940699253 + 20 +0.05495760346078548 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1A5 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.28019485965345087 + 20 +0.054656136115834704 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1A6 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.28103201462771693 + 20 +0.054343436540721615 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1A7 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2818613744481334 + 20 +0.05401951674289469 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1A8 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2826821545701103 + 20 +0.053684388729802406 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1A9 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.28349357044905754 + 20 +0.05333806450889323 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1AA +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.28429483754038504 + 20 +0.05298055608761565 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1AB +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.28508517129950284 + 20 +0.052611875473418133 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1AC +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.28586378718182087 + 20 +0.05223203467374917 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1AD +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.286629900642749 + 20 +0.051841045696057235 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1AE +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2873827271376973 + 20 +0.0514389205477908 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1AF +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.28812148212207567 + 20 +0.05102567123639837 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1B0 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.28884538105129404 + 20 +0.050601309769328376 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1B1 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.28955363938076245 + 20 +0.05016584815402933 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1B2 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2902454725658908 + 20 +0.04971929839794969 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1B3 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2909200960620891 + 20 +0.04926167250853794 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1B4 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.29157672532476714 + 20 +0.04879298249324257 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1B5 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2922145758093351 + 20 +0.048313240359512045 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1B6 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.29283286297120276 + 20 +0.047822458114794866 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1B7 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.29343080226578017 + 20 +0.04732064776653947 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1B8 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2940076091484773 + 20 +0.04680782132219436 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1B9 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.294562499074704 + 20 +0.04628399078920801 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1BA +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.29509468749987033 + 20 +0.045749168175028894 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1BB +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2956033898793862 + 20 +0.04520336548710549 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1BC +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2960878216686616 + 20 +0.04464659473288631 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1BD +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.29654719832310644 + 20 +0.04407886791981978 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1BE +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2969807352981307 + 20 +0.04350019705535439 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1BF +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.29738764804914436 + 20 +0.042910594146938624 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1C0 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.29776715203155735 + 20 +0.042310071202020974 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1C1 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2981184627007796 + 20 +0.041698640228049894 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1C2 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2984407955122211 + 20 +0.04107631323247386 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1C3 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2987333659212918 + 20 +0.04044310222274142 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1C4 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.29899538938340164 + 20 +0.03979901920630096 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1C5 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.29922608135396056 + 20 +0.03914407619060099 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1C6 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2994246572883786 + 20 +0.03847828518308999 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1C7 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.29959033264206564 + 20 +0.037801658191216427 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1C8 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.29972232287043166 + 20 +0.03711420722242881 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1C9 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2998198434288866 + 20 +0.03641594428417562 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1CA +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.29989971397700854 + 20 +0.035572126171891955 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1CB +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.29995773683528665 + 20 +0.03469691872510711 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1CC +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2999909354403105 + 20 +0.03382626096445765 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1CD +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2999997914953523 + 20 +0.032960375780174786 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1CE +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.29998478670368384 + 20 +0.03209948606248974 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1CF +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.29994640276857737 + 20 +0.031243814701633746 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1D0 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2998851213933048 + 20 +0.03039358458783807 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1D1 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.29980142428113826 + 20 +0.029549018611333862 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1D2 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.29969579313534966 + 20 +0.028710339662352376 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1D3 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.29956870965921106 + 20 +0.02787777063112485 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1D4 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2994206555559945 + 20 +0.027051534407882508 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1D5 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.29925211252897205 + 20 +0.026231853882856572 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1D6 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2990635622814157 + 20 +0.025418951946278273 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1D7 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.29885548651659744 + 20 +0.02461305148837887 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1D8 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2986283669377893 + 20 +0.023814375399389513 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1D9 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2983826852482634 + 20 +0.023023146569541478 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1DA +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2981189231512917 + 20 +0.022239587889065972 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1DB +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2978375623501462 + 20 +0.02146392224819424 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1DC +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.29753908454809896 + 20 +0.020696372537157497 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1DD +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.29722397144842205 + 20 +0.019937161646186974 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1DE +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.29689270475438745 + 20 +0.019186512465513887 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1DF +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.29654576616926714 + 20 +0.018444647885369467 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1E0 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.29618363739633324 + 20 +0.017711790795984957 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1E1 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2958068001388578 + 20 +0.016988164087591635 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1E2 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2954157361001127 + 20 +0.016273990650420588 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1E3 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.29501092698337006 + 20 +0.01556949337470312 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1E4 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2945928544919019 + 20 +0.014874895150670454 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1E5 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2941620003289803 + 20 +0.014190418868553815 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1E6 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2937188461978772 + 20 +0.013516287418584433 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1E7 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.29326387380186464 + 20 +0.01285272369099353 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1E8 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2927975648442147 + 20 +0.012199950576012322 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1E9 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2923204010281994 + 20 +0.011558190963872061 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1EA +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2918328640570907 + 20 +0.010927667744803943 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1EB +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2913354356341607 + 20 +0.010308603809039224 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1EC +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.29082859746268136 + 20 +0.009701222046809121 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1ED +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2903128312459248 + 20 +0.009105745348344838 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1EE +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.289788618687163 + 20 +0.008522396603877679 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1EF +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.28925644148966795 + 20 +0.007951398703638764 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1F0 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2887167813567117 + 20 +0.007392974537859351 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1F1 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2881701199915663 + 20 +0.006847346996770691 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1F2 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.28761693909750374 + 20 +0.006314738970604 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1F3 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2870577203777961 + 20 +0.005795373349590495 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1F4 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.28649294553571536 + 20 +0.005289473023961419 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1F5 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2859230962745336 + 20 +0.004797260883947975 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1F6 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.28534865429752276 + 20 +0.0043189598197814205 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1F7 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.284770101307955 + 20 +0.003854792721692965 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1F8 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2841879190091022 + 20 +0.0034049824799138317 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1F9 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.28360258910423647 + 20 +0.0029697519846752438 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1FA +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2830145932966298 + 20 +0.002549324126208438 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1FB +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.28242441328955437 + 20 +0.00214392179474468 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1FC +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.28183253078628195 + 20 +0.0017537678805151016 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1FD +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.28123942749008474 + 20 +0.0013790852737509823 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1FE +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.28064558510423465 + 20 +0.001020096864683559 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +1FF +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.28005148533200386 + 20 +0.0006770255435440409 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +200 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2794576098766643 + 20 +0.00035009420056365814 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +201 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2785802222856699 + 20 +-0.00011148951773121141 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +202 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2777085656861816 + 20 +-0.0005522300202456462 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +203 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2768600535620914 + 20 +-0.0009640968413820866 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +204 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.27603322577419515 + 20 +-0.0013491701503893783 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +205 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.27522662218328836 + 20 +-0.0017095301165163876 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +206 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.27443878265016663 + 20 +-0.0020472569090119394 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +207 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.27366824703562553 + 20 +-0.0023644306971249 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +208 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.27291355520046084 + 20 +-0.0026631316501040878 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +209 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2721732470054679 + 20 +-0.0029454399371984033 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +20A +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2714458623114425 + 20 +-0.0032134357276566855 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +20B +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2707299409791802 + 20 +-0.003469199190727766 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +20C +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2700240228694765 + 20 +-0.0037148104956605046 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +20D +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.26932664784312715 + 20 +-0.003952349811703747 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +20E +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2686363557609277 + 20 +-0.004183897308106345 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +20F +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2679516864836738 + 20 +-0.004411533154117138 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +210 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.267271179872161 + 20 +-0.004637337518984992 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +211 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.26659337578718484 + 20 +-0.004863390571958746 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +212 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.26591681408954104 + 20 +-0.005091772482287253 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +213 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.26524003464002516 + 20 +-0.005324563419219379 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +214 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.26456157729943286 + 20 +-0.005563843552003948 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +215 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.26387998192855966 + 20 +-0.005811693049889828 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +216 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.26319378838820123 + 20 +-0.006070192082125829 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +217 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2625015365391531 + 20 +-0.006341420817960873 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +218 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2618017662422109 + 20 +-0.006627459426643757 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +219 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.26109301735817025 + 20 +-0.006930388077423348 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +21A +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2603738297478268 + 20 +-0.007252286939548498 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +21B +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2596427432719761 + 20 +-0.007595236182268046 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +21C +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2588982977914137 + 20 +-0.007961315974830858 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +21D +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.25813903316693537 + 20 +-0.00835260648648576 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +21E +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2573045899793085 + 20 +-0.008806547854173986 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +21F +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2564582507712165 + 20 +-0.009302596558563934 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +220 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2556403556510659 + 20 +-0.009818998684686171 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +221 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.25485004869347944 + 20 +-0.010354882190157184 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +222 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.25408647397308 + 20 +-0.010909375032593448 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +223 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2533487755644903 + 20 +-0.011481605169611435 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +224 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.25263609754233307 + 20 +-0.01207070055882773 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +225 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2519475839812312 + 20 +-0.012675789157858781 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +226 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2512823789558074 + 20 +-0.013295998924321074 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +227 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.25063962654068456 + 20 +-0.013930457815831111 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +228 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2500184708104854 + 20 +-0.014578293790005366 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +229 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.24941805583983273 + 20 +-0.015238634804460367 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +22A +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2488375257033493 + 20 +-0.01591060881681259 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +22B +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.24827602447565794 + 20 +-0.01659334378467852 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +22C +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.24773269623138144 + 20 +-0.01728596766567466 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +22D +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2472066850451426 + 20 +-0.017987608417417512 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +22E +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.24669713499156418 + 20 +-0.018697393997523577 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +22F +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.24620319014526898 + 20 +-0.019414452363609315 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +230 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.24572399458087985 + 20 +-0.02013791147329117 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +231 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.24525869237301948 + 20 +-0.020866899284185773 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +232 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2448064275963107 + 20 +-0.021600543753909567 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +233 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.24436634432537627 + 20 +-0.022337972840079025 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +234 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.24393758663483905 + 20 +-0.02307831450031065 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +235 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.24351929859932178 + 20 +-0.02382069669222093 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +236 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.24311062429344726 + 20 +-0.024564247373426364 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +237 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2427107077918383 + 20 +-0.025308094501543442 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +238 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.24231869316911764 + 20 +-0.026051366034188665 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +239 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.24193372449990813 + 20 +-0.02679318992897852 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +23A +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2415549458588325 + 20 +-0.02753269414352951 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +23B +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2411815013205136 + 20 +-0.028269006635458123 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +23C +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2408125349595742 + 20 +-0.029001255362380844 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +23D +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2404471908506371 + 20 +-0.029728568281914135 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +23E +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.24008461306832501 + 20 +-0.03045007335167458 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +23F +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.23972394568726083 + 20 +-0.031164898529278638 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +240 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.23936433278206726 + 20 +-0.03187217177234277 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +241 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.23900491842736715 + 20 +-0.03257102103848351 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +242 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2386448466977833 + 20 +-0.03326057428531733 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +243 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.23828326166793845 + 20 +-0.033939959470460725 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +244 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2379193074124554 + 20 +-0.034608304551530196 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +245 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.23755212800595693 + 20 +-0.03526473748614222 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +246 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.23718086752306589 + 20 +-0.0359083862319133 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +247 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.23680467003840502 + 20 +-0.03653837874645995 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +248 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.23642267962659713 + 20 +-0.03715384298739865 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +249 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.236034040362265 + 20 +-0.037753906912345904 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +24A +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2356378963200314 + 20 +-0.03833769847891819 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +24B +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.23523339157451917 + 20 +-0.03890434564473195 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +24C +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.23481967020035105 + 20 +-0.03945297636740377 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +24D +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.23439587627214986 + 20 +-0.039982718604550126 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +24E +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.23396115386453836 + 20 +-0.04049270031378751 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +24F +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.23351464705213937 + 20 +-0.04098204945273238 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +250 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2330554999095757 + 20 +-0.041449893979001264 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +251 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.23258285651147007 + 20 +-0.04189536185021065 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +252 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.23209586093244533 + 20 +-0.042317581023977015 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +253 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.23159365724712422 + 20 +-0.04271567945791687 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +254 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.23107538953012957 + 20 +-0.04308878510964669 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +255 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.23054020185608418 + 20 +-0.04343602593678301 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +256 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.22998723829961082 + 20 +-0.043756529896942266 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +257 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.22941564293533223 + 20 +-0.04404942494774102 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +258 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.22882455983787137 + 20 +-0.04431383904679568 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +259 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.22821313308185082 + 20 +-0.04454890015172281 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +25A +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.22758050674189345 + 20 +-0.0447537362201389 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +25B +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.22634914199949607 + 20 +-0.04509063202675693 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +25C +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.22487076492189773 + 20 +-0.04548455689656774 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +25D +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2235075582558282 + 20 +-0.04583839999248406 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +25E +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.22225232938837636 + 20 +-0.046153793603088 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +25F +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.22109788570663097 + 20 +-0.046432370016961634 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +260 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.22003703459768087 + 20 +-0.04667576152268709 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +261 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.21906258344861482 + 20 +-0.04688560040884641 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +262 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.21816733964652166 + 20 +-0.047063518964021715 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +263 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.21734411057849018 + 20 +-0.04721114947679509 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +264 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.21658570363160917 + 20 +-0.04733012423574864 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +265 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2158849261929675 + 20 +-0.04742207552946442 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +266 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2152345856496539 + 20 +-0.04748863564652455 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +267 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.21462748938875717 + 20 +-0.04753143687551114 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +268 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.21405644479736613 + 20 +-0.04755211150500626 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +269 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.21351425926256956 + 20 +-0.04755229182359199 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +26A +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.21299374017145634 + 20 +-0.04753361011985046 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +26B +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2124876949111152 + 20 +-0.04749769868236372 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +26C +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.21198893086863496 + 20 +-0.047446189799713885 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +26D +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.21149025543110445 + 20 +-0.04738071576048302 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +26E +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.21098447598561243 + 20 +-0.04730290885325327 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +26F +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.21046439991924773 + 20 +-0.047214401366606686 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +270 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.20992283461909914 + 20 +-0.047116825589125354 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +271 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2093525874722555 + 20 +-0.047011813809391394 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +272 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.20874646586580556 + 20 +-0.046900998315986896 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +273 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2081133859820591 + 20 +-0.04678880157640792 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +274 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.20797595180754558 + 20 +-0.04676409716209739 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +275 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.20782347651287447 + 20 +-0.04673509760131351 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +276 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.20765596008657225 + 20 +-0.04670180290552981 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +277 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.20747340251716528 + 20 +-0.04666421308621991 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +278 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.20727580379318003 + 20 +-0.04662232815485736 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +279 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2070631639031429 + 20 +-0.04657614812291578 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +27A +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.20683548283558031 + 20 +-0.04652567300186871 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +27B +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.20659276057901868 + 20 +-0.04647090280318975 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +27C +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.20633499712198441 + 20 +-0.04641183753835248 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +27D +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.20606219245300395 + 20 +-0.04634847721883046 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +27E +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.20577434656060375 + 20 +-0.04628082185609731 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +27F +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.20547145943331013 + 20 +-0.04620887146162656 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +280 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.20515353105964967 + 20 +-0.04613262604689184 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +281 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.20482056142814864 + 20 +-0.04605208562336669 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +282 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.20447255052733349 + 20 +-0.04596725020252471 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +283 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.20410949834573067 + 20 +-0.04587811979583949 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +284 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2037314048718666 + 20 +-0.045784694414784576 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +285 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2033382700942677 + 20 +-0.04568697407083356 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +286 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.2029300940014604 + 20 +-0.04558495877546002 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +287 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.20250687658197108 + 20 +-0.04547864854013756 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +288 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.20206861782432617 + 20 +-0.045368043376339755 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +289 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.20161531771705216 + 20 +-0.04525314329554015 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +28A +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.20114697624867536 + 20 +-0.04513394830921235 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +28B +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.20066359340772227 + 20 +-0.045010458428829926 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +28C +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.20016516918271926 + 20 +-0.04488267366586648 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +28D +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.19965170356219286 + 20 +-0.04475059403179558 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +28E +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.19912319653466934 + 20 +-0.0446142195380908 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +28F +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.19857964808867518 + 20 +-0.04447355019622571 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +290 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.19802105821273683 + 20 +-0.0443285860176739 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +291 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1974474268953807 + 20 +-0.044179327013908955 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +292 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.19685875412513315 + 20 +-0.044025773196404445 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +293 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.19625503989052068 + 20 +-0.04386792457663396 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +294 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.19563628418006965 + 20 +-0.04370578116607108 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +295 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.19500248698230654 + 20 +-0.04353934297618937 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +296 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1943536482857577 + 20 +-0.04336861001846242 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +297 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.19368976807894964 + 20 +-0.04319358230436381 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +298 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1930108463504087 + 20 +-0.04301425984536713 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +299 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.19231688308866132 + 20 +-0.04283064265294593 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +29A +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.191607878282234 + 20 +-0.04264273073857384 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +29B +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.19088383191965302 + 20 +-0.042450524113724374 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +29C +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1901447439894449 + 20 +-0.04225402278987118 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +29D +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.189390614480136 + 20 +-0.042053226778487766 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +29E +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1886214433802528 + 20 +-0.04184813609104777 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +29F +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.18783723067832167 + 20 +-0.04163875073902474 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2A0 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.18703797636286904 + 20 +-0.04142507073389227 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2A1 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.18622368042242138 + 20 +-0.04120709608712394 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2A2 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.18539434284550504 + 20 +-0.040984826810193324 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2A3 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.18454996362064646 + 20 +-0.040758262914573996 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2A4 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1836905427363721 + 20 +-0.040527404411739555 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2A5 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.18281608018120835 + 20 +-0.04029225131316356 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2A6 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1819265759436816 + 20 +-0.04005280363031961 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2A7 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1810220300123183 + 20 +-0.03980906137468127 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2A8 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.18010244237564504 + 20 +-0.03956102455772214 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2A9 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.17916781302218793 + 20 +-0.03930869319091578 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2AA +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.17821814194047353 + 20 +-0.03905206728573574 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2AB +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.17725342911902828 + 20 +-0.03879114685365568 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2AC +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1762736745463786 + 20 +-0.038525931906149107 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2AD +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.17527887821105081 + 20 +-0.038256422454689624 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2AE +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.17426904010157152 + 20 +-0.03798261851075082 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2AF +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.173244160206467 + 20 +-0.03770452008580626 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2B0 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1722042385142637 + 20 +-0.037422127191329556 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2B1 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.17114927501348806 + 20 +-0.037135439838794256 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2B2 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.17007926969266654 + 20 +-0.03684445803967392 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2B3 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.16899422254032548 + 20 +-0.03654918180544219 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2B4 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.16789413354499133 + 20 +-0.03624961114757259 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2B5 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1667790026951906 + 20 +-0.03594574607753876 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2B6 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.16564882997944957 + 20 +-0.035637586606814216 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2B7 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1645036153862947 + 20 +-0.03532513274687255 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2B8 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.16334335890425244 + 20 +-0.03500838450918737 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2B9 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.16216806052184918 + 20 +-0.034687341905232226 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2BA +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.16097772022761137 + 20 +-0.034362004946480736 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2BB +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1597723380100654 + 20 +-0.03403237364440643 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2BC +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1585519138577377 + 20 +-0.03369844801048292 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2BD +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.15731644775915474 + 20 +-0.03336022805618378 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2BE +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.15606593970284288 + 20 +-0.03301771379298258 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2BF +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.15480038967732856 + 20 +-0.03267090523235292 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2C0 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.15351979767113816 + 20 +-0.03231980238576836 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2C1 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1522241636727982 + 20 +-0.031964405264702504 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2C2 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.15091348767083515 + 20 +-0.031604713880628944 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2C3 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.14958776965377518 + 20 +-0.031240728245021186 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2C4 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.14824700961014486 + 20 +-0.030872448369352858 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2C5 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1468912075284706 + 20 +-0.030499874265097544 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2C6 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.14552036339727878 + 20 +-0.03012300594372882 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2C7 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1441344772050959 + 20 +-0.029741843416720253 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2C8 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.14273354894044837 + 20 +-0.029356386695545432 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2C9 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.14194240269108277 + 20 +-0.02908606215953797 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2CA +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.14119068819494135 + 20 +-0.028823231876943294 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2CB +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.14043897369879987 + 20 +-0.028560401594348604 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2CC +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.13968725920265843 + 20 +-0.02829757131175393 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2CD +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.138935544706517 + 20 +-0.02803474102915924 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2CE +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.13818383021037556 + 20 +-0.027771910746564564 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2CF +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.13743211571423417 + 20 +-0.027509080463969916 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2D0 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.13668040121809272 + 20 +-0.027246250181375227 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2D1 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.13592868672195127 + 20 +-0.02698341989878055 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2D2 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1351769722258098 + 20 +-0.026720589616185862 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2D3 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.13442525772966837 + 20 +-0.026457759333591187 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2D4 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.13367354323352693 + 20 +-0.026194929050996497 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2D5 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.13292182873738545 + 20 +-0.025932098768401822 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2D6 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.13217011424124403 + 20 +-0.025669268485807133 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2D7 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.13141839974510258 + 20 +-0.025406438203212457 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2D8 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.13066668524896113 + 20 +-0.025143607920617768 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2D9 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1299149707528197 + 20 +-0.024880777638023092 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2DA +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.12916325625667824 + 20 +-0.024617947355428403 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2DB +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1284115417605368 + 20 +-0.024355117072833728 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2DC +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.12765982726439531 + 20 +-0.02409228679023904 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2DD +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.12690811276825395 + 20 +-0.023829456507644377 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2DE +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.12615639827211253 + 20 +-0.0235666262250497 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2DF +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.12540468377597105 + 20 +-0.023303795942455012 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2E0 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1246529692798296 + 20 +-0.023040965659860337 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2E1 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.12390125478368816 + 20 +-0.022778135377265647 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2E2 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.12314954028754671 + 20 +-0.022515305094670972 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2E3 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.12239782579140526 + 20 +-0.022252474812076282 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2E4 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.12164611129526381 + 20 +-0.021989644529481607 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2E5 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1208943967991224 + 20 +-0.02172681424688693 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2E6 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.12014268230298092 + 20 +-0.021463983964292242 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2E7 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.11939096780683947 + 20 +-0.021201153681697553 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2E8 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.11863925331069805 + 20 +-0.020938323399102877 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2E9 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.11788753881455658 + 20 +-0.020675493116508202 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2EA +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.11713582431841518 + 20 +-0.020412662833913554 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2EB +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.11638410982227376 + 20 +-0.02014983255131885 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2EC +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.11563239532613229 + 20 +-0.01988700226872419 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2ED +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.11488068082999084 + 20 +-0.019624171986129486 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2EE +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.11412896633384942 + 20 +-0.019361341703534797 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2EF +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.11337725183770794 + 20 +-0.01909851142094012 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2F0 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1126255373415665 + 20 +-0.01883568113834546 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2F1 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.11187382284542507 + 20 +-0.018572850855750757 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2F2 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.11112210834928363 + 20 +-0.018310020573156095 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2F3 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.11037039385314215 + 20 +-0.018047190290561392 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2F4 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1096186793570007 + 20 +-0.01778436000796673 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2F5 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.10886696486085926 + 20 +-0.017521529725372027 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2F6 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.10811525036471781 + 20 +-0.017258699442777366 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2F7 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.10736353586857644 + 20 +-0.016995869160182704 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2F8 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.106611821372435 + 20 +-0.016733038877588015 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2F9 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.10586010687629352 + 20 +-0.016470208594993326 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2FA +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.1051083923801521 + 20 +-0.01620737831239865 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2FB +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.10435667788401065 + 20 +-0.015944548029803975 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2FC +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.10360496338786918 + 20 +-0.015681717747209285 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2FD +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.10285324889172773 + 20 +-0.01541888746461461 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2FE +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.10210153439558631 + 20 +-0.01515605718201992 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +2FF +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.10134981989944486 + 20 +-0.014893226899425245 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +300 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.10059810540330338 + 20 +-0.014630396616830556 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +301 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.09984639090716196 + 20 +-0.01436756633423588 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +302 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.09909467641102052 + 20 +-0.014104736051641191 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +303 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.09834296191487904 + 20 +-0.013841905769046516 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +304 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.09759124741873768 + 20 +-0.013579075486451854 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +305 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.09683953292259623 + 20 +-0.013316245203857165 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +306 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.09608781842645481 + 20 +-0.01305341492126249 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +307 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.09533610393031333 + 20 +-0.012790584638667814 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +308 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.09458438943417188 + 20 +-0.012527754356073124 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +309 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.09383267493803041 + 20 +-0.012264924073478449 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +30A +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.09308096044188899 + 20 +-0.01200209379088376 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +30B +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.09232924594574754 + 20 +-0.011739263508289084 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +30C +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.09157753144960609 + 20 +-0.011476433225694395 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +30D +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.09082581695346464 + 20 +-0.01121360294309972 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +30E +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.0900741024573232 + 20 +-0.01095077266050503 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +30F +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.08932238796118175 + 20 +-0.010687942377910355 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +310 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.08857067346504027 + 20 +-0.010425112095315665 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +311 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.08781339995867424 + 20 +-0.010179066664029617 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +312 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.08705340728699001 + 20 +-0.00994123146868598 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +313 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.08629341461530574 + 20 +-0.009703396273342331 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +314 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.0855334219436214 + 20 +-0.009465561077998681 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +315 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.0847734292719371 + 20 +-0.009227725882655018 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +316 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.08401343660025282 + 20 +-0.008989890687311368 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +317 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.08325344392856851 + 20 +-0.008752055491967718 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +318 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.08249345125688423 + 20 +-0.008514220296624068 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +319 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.0817334585851999 + 20 +-0.008276385101280419 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +31A +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.08097346591351562 + 20 +-0.008038549905936769 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +31B +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.08021347324183131 + 20 +-0.0078007147105931054 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +31C +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.079453480570147 + 20 +-0.007562879515249456 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +31D +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.07869348789846273 + 20 +-0.007325044319905792 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +31E +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.07793349522677842 + 20 +-0.007087209124562142 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +31F +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.0771735025550942 + 20 +-0.0068493739292185205 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +320 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.07641350988340989 + 20 +-0.006611538733874871 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +321 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.07565351721172561 + 20 +-0.006373703538531221 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +322 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.07489352454004128 + 20 +-0.006135868343187557 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +323 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.07413353186835697 + 20 +-0.005898033147843908 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +324 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.0733735391966727 + 20 +-0.005660197952500258 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +325 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.07261354652498839 + 20 +-0.005422362757156594 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +326 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.07185355385330411 + 20 +-0.005184527561812945 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +327 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.0710935611816198 + 20 +-0.004946692366469295 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +328 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.07033356850993547 + 20 +-0.004708857171125645 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +329 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.06957357583825119 + 20 +-0.004471021975781982 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +32A +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.06881358316656688 + 20 +-0.004233186780438332 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +32B +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.0680535904948826 + 20 +-0.003995351585094682 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +32C +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.06729359782319835 + 20 +-0.0037575163897510602 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +32D +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.06653360515151407 + 20 +-0.0035196811944074036 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +32E +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.06577361247982977 + 20 +-0.003281845999063747 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +32F +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.06501361980814549 + 20 +-0.0030440108037200903 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +330 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.06425362713646118 + 20 +-0.0028061756083764405 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +331 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.06349363446477685 + 20 +-0.002568340413032791 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +332 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.06273364179309257 + 20 +-0.002330505217689134 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +333 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.061973649121408264 + 20 +-0.0020926700223454844 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +334 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.06121365644972396 + 20 +-0.0018548348270018278 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +335 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.060504285896758825 + 20 +-0.0016749033629925442 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +336 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.05982983194144559 + 20 +-0.0015185883853187018 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +337 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.05916547701376473 + 20 +-0.0013568687471811197 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +338 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.05850873972343856 + 20 +-0.0011897785906160516 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +339 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.05785713868018941 + 20 +-0.0010173520576597928 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +33A +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.05720819249373971 + 20 +-0.0008396232903486317 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +33B +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.05655941977381171 + 20 +-0.0006566264307188152 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +33C +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.05590833913012777 + 20 +-0.0004683956208066317 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +33D +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.05525246917241025 + 20 +-0.0002749650026483627 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +33E +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.05458932851038148 + 20 +-7.636871828028274e-05 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +33F +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.05391643575376387 + 20 +0.00012735909026131975 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +340 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.05323130951227964 + 20 +0.0003361842809401841 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +341 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.05253146839565126 + 20 +0.0005500727117200149 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +342 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.051814431013601 + 20 +0.0007689902405645516 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +343 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.05107771597585123 + 20 +0.0009929027254375056 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +344 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.05031884189212435 + 20 +0.0012217760243026024 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +345 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.04953532737214256 + 20 +0.0014555759951235675 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +346 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.04872469102562832 + 20 +0.0016942684958641194 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +347 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.04788445146230408 + 20 +0.0019378193844879557 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +348 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.047012127291891914 + 20 +0.0021861945189588436 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +349 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.04610523712411435 + 20 +0.0024393597572404946 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +34A +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.04516129956869369 + 20 +0.002697280957296627 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +34B +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.04417783323535224 + 20 +0.002959923977090953 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +34C +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.0431523567338124 + 20 +0.0032272546745872044 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +34D +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.04208238867379649 + 20 +0.0034992389077491 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +34E +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.0414744415029098 + 20 +0.0034866198640948268 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +34F +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.040885322957459835 + 20 +0.003433465737780486 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +350 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.040252175894184894 + 20 +0.0033764242771729958 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +351 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.039577151513876696 + 20 +0.0033156915003708515 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +352 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.03886240101732691 + 20 +0.003251463425472542 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +353 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.038110075605327254 + 20 +0.0031839360705766054 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +354 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.037322326478669454 + 20 +0.003113305453781523 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +355 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.03650130483814512 + 20 +0.0030397675931858117 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +356 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.03564916188454598 + 20 +0.0029635185068879605 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +357 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.03476804881866369 + 20 +0.002884754212986493 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +358 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.03386011684129003 + 20 +0.0028036707295799185 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +359 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.03292751715321657 + 20 +0.0027204640747667264 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +35A +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.03197240095523507 + 20 +0.002635330266645433 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +35B +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.03099691944813726 + 20 +0.0025484653233145416 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +35C +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.030003223832714754 + 20 +0.0024600652628725683 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +35D +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.02899346530975938 + 20 +0.0023703261034180023 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +35E +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.027969795080062643 + 20 +0.00227944386304936 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +35F +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.02693436434441626 + 20 +0.0021876145598651586 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +360 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.02588932430361207 + 20 +0.0020950342119638726 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +361 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.024836826158441794 + 20 +0.0020018988374440466 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +362 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.023779021109696874 + 20 +0.0019084044544041556 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +363 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.022718060358169145 + 20 +0.0018147470809427232 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +364 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.021656095104650275 + 20 +0.0017211227351582384 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +365 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.020595276549931985 + 20 +0.0016277274351492246 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +366 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.019537755894805886 + 20 +0.0015347571990141848 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +367 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.018485684340063813 + 20 +0.0014424080448516147 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +368 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.017441213086497376 + 20 +0.001350875990760038 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +369 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.016406493334898242 + 20 +0.0012603570548379434 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +36A +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.015383676286058134 + 20 +0.0011710472551838408 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +36B +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.01437491314076872 + 20 +0.0010831426098962468 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +36C +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.013382355099821719 + 20 +0.0009968391370736573 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +36D +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.012408153364008856 + 20 +0.000912332854814582 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +36E +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.011454459134121742 + 20 +0.0008298197812175306 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +36F +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.01052342361095221 + 20 +0.0007494959343810059 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +370 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.00961719799529176 + 20 +0.0006715573324035107 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +371 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.00873793348793217 + 20 +0.0005961999933835546 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +372 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.007887781289665108 + 20 +0.0005236199354196405 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +373 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.007068892601282295 + 20 +0.0004540131766102712 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +374 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.006283418623575454 + 20 +0.00038757573505396326 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +375 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.0055335105573361965 + 20 +0.0003245036288492195 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +376 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.0048213196033563 + 20 +0.0002649928760945497 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +377 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.0041489969624274314 + 20 +0.00020923949488844268 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +378 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.0035186938353412023 + 20 +0.00015743950332942902 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +379 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.0029325614228893904 + 20 +0.00010978891951599068 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +37A +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.0023927509258636626 + 20 +6.648376154665125e-05 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +37B +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.0019014135450557412 + 20 +2.7720047519913527e-05 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +37C +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.0014607004812572932 + 20 +-6.3062044657197225e-06 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +37D +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.0010727629352599855 + 20 +-3.539897631173877e-05 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +37E +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.0007397521078555402 + 20 +-5.936224991964778e-05 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +37F +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.00046381919983556896 + 20 +-7.800000719092315e-05 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +380 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.000247115411991905 + 20 +-9.111623002706903e-05 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +381 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-9.179194511615973e-05 + 20 +-9.85149003295896e-05 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +382 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +0.0 + 20 +-9.999999999996123e-05 + 30 +0.0 + 70 +0 + 0 +SEQEND + 5 +30 +330 +2F +100 +AcDbEntity + 8 +0 + 0 +ENDSEC + 0 +SECTION + 2 +OBJECTS + 0 +DICTIONARY + 5 +A +330 +0 +100 +AcDbDictionary +281 +1 + 3 +ACAD_COLOR +350 +B + 3 +ACAD_GROUP +350 +C + 3 +ACAD_LAYOUT +350 +D + 3 +ACAD_MATERIAL +350 +E + 3 +ACAD_MLEADERSTYLE +350 +F + 3 +ACAD_MLINESTYLE +350 +10 + 3 +ACAD_PLOTSETTINGS +350 +11 + 3 +ACAD_PLOTSTYLENAME +350 +12 + 3 +ACAD_SCALELIST +350 +14 + 3 +ACAD_TABLESTYLE +350 +15 + 3 +ACAD_VISUALSTYLE +350 +16 + 3 +EZDXF_META +350 +2D + 0 +DICTIONARY + 5 +B +330 +A +100 +AcDbDictionary +281 +1 + 0 +DICTIONARY + 5 +C +330 +A +100 +AcDbDictionary +281 +1 + 0 +DICTIONARY + 5 +D +330 +A +100 +AcDbDictionary +281 +1 + 3 +Model +350 +1A + 3 +Layout1 +350 +1E + 0 +DICTIONARY + 5 +E +330 +A +100 +AcDbDictionary +281 +1 + 3 +ByBlock +350 +1F + 3 +ByLayer +350 +20 + 3 +Global +350 +21 + 0 +DICTIONARY + 5 +F +330 +A +100 +AcDbDictionary +281 +1 + 3 +Standard +350 +2C + 0 +DICTIONARY + 5 +10 +330 +A +100 +AcDbDictionary +281 +1 + 3 +Standard +350 +22 + 0 +DICTIONARY + 5 +11 +330 +A +100 +AcDbDictionary +281 +1 + 0 +ACDBDICTIONARYWDFLT + 5 +12 +330 +A +100 +AcDbDictionary +281 +1 + 3 +Normal +350 +13 +100 +AcDbDictionaryWithDefault +340 +13 + 0 +ACDBPLACEHOLDER + 5 +13 +330 +12 + 0 +DICTIONARY + 5 +14 +330 +A +100 +AcDbDictionary +281 +1 + 0 +DICTIONARY + 5 +15 +330 +A +100 +AcDbDictionary +281 +1 + 0 +DICTIONARY + 5 +16 +330 +A +100 +AcDbDictionary +281 +1 + 0 +LAYOUT + 5 +1A +330 +D +100 +AcDbPlotSettings + 1 + + 4 +A3 + 6 + + 40 +7.5 + 41 +20.0 + 42 +7.5 + 43 +20.0 + 44 +420.0 + 45 +297.0 + 46 +0.0 + 47 +0.0 + 48 +0.0 + 49 +0.0 +140 +0.0 +141 +0.0 +142 +1.0 +143 +1.0 + 70 +1024 + 72 +1 + 73 +0 + 74 +5 + 7 + + 75 +16 + 76 +0 + 77 +2 + 78 +300 +147 +1.0 +148 +0.0 +149 +0.0 +100 +AcDbLayout + 1 +Model + 70 +1 + 71 +0 + 10 +0.0 + 20 +0.0 + 11 +420.0 + 21 +297.0 + 12 +0.0 + 22 +0.0 + 32 +0.0 + 14 +1e+20 + 24 +1e+20 + 34 +1e+20 + 15 +-1e+20 + 25 +-1e+20 + 35 +-1e+20 +146 +0.0 + 13 +0.0 + 23 +0.0 + 33 +0.0 + 16 +1.0 + 26 +0.0 + 36 +0.0 + 17 +0.0 + 27 +1.0 + 37 +0.0 + 76 +1 +330 +17 + 0 +LAYOUT + 5 +1E +330 +D +100 +AcDbPlotSettings + 1 + + 4 +A3 + 6 + + 40 +7.5 + 41 +20.0 + 42 +7.5 + 43 +20.0 + 44 +420.0 + 45 +297.0 + 46 +0.0 + 47 +0.0 + 48 +0.0 + 49 +0.0 +140 +0.0 +141 +0.0 +142 +1.0 +143 +1.0 + 70 +0 + 72 +1 + 73 +0 + 74 +5 + 7 + + 75 +16 + 76 +0 + 77 +2 + 78 +300 +147 +1.0 +148 +0.0 +149 +0.0 +100 +AcDbLayout + 1 +Layout1 + 70 +1 + 71 +1 + 10 +0.0 + 20 +0.0 + 11 +420.0 + 21 +297.0 + 12 +0.0 + 22 +0.0 + 32 +0.0 + 14 +1e+20 + 24 +1e+20 + 34 +1e+20 + 15 +-1e+20 + 25 +-1e+20 + 35 +-1e+20 +146 +0.0 + 13 +0.0 + 23 +0.0 + 33 +0.0 + 16 +1.0 + 26 +0.0 + 36 +0.0 + 17 +0.0 + 27 +1.0 + 37 +0.0 + 76 +1 +330 +1B + 0 +MATERIAL + 5 +1F +102 +{ACAD_REACTORS +330 +E +102 +} +330 +E +100 +AcDbMaterial + 1 +ByBlock + 2 + + 70 +0 + 40 +1.0 + 71 +1 + 41 +1.0 + 91 +-1023410177 + 42 +1.0 + 72 +1 + 3 + + 73 +1 + 74 +1 + 75 +1 + 44 +0.5 + 73 +0 + 45 +1.0 + 46 +1.0 + 77 +1 + 4 + + 78 +1 + 79 +1 +170 +1 + 48 +1.0 +171 +1 + 6 + +172 +1 +173 +1 +174 +1 +140 +1.0 +141 +1.0 +175 +1 + 7 + +176 +1 +177 +1 +178 +1 +143 +1.0 +179 +1 + 8 + +270 +1 +271 +1 +272 +1 +145 +1.0 +146 +1.0 +273 +1 + 9 + +274 +1 +275 +1 +276 +1 + 42 +1.0 + 72 +1 + 3 + + 73 +1 + 74 +1 + 75 +1 + 94 +63 + 0 +MATERIAL + 5 +20 +102 +{ACAD_REACTORS +330 +E +102 +} +330 +E +100 +AcDbMaterial + 1 +ByLayer + 2 + + 70 +0 + 40 +1.0 + 71 +1 + 41 +1.0 + 91 +-1023410177 + 42 +1.0 + 72 +1 + 3 + + 73 +1 + 74 +1 + 75 +1 + 44 +0.5 + 73 +0 + 45 +1.0 + 46 +1.0 + 77 +1 + 4 + + 78 +1 + 79 +1 +170 +1 + 48 +1.0 +171 +1 + 6 + +172 +1 +173 +1 +174 +1 +140 +1.0 +141 +1.0 +175 +1 + 7 + +176 +1 +177 +1 +178 +1 +143 +1.0 +179 +1 + 8 + +270 +1 +271 +1 +272 +1 +145 +1.0 +146 +1.0 +273 +1 + 9 + +274 +1 +275 +1 +276 +1 + 42 +1.0 + 72 +1 + 3 + + 73 +1 + 74 +1 + 75 +1 + 94 +63 + 0 +MATERIAL + 5 +21 +102 +{ACAD_REACTORS +330 +E +102 +} +330 +E +100 +AcDbMaterial + 1 +Global + 2 + + 70 +0 + 40 +1.0 + 71 +1 + 41 +1.0 + 91 +-1023410177 + 42 +1.0 + 72 +1 + 3 + + 73 +1 + 74 +1 + 75 +1 + 44 +0.5 + 73 +0 + 45 +1.0 + 46 +1.0 + 77 +1 + 4 + + 78 +1 + 79 +1 +170 +1 + 48 +1.0 +171 +1 + 6 + +172 +1 +173 +1 +174 +1 +140 +1.0 +141 +1.0 +175 +1 + 7 + +176 +1 +177 +1 +178 +1 +143 +1.0 +179 +1 + 8 + +270 +1 +271 +1 +272 +1 +145 +1.0 +146 +1.0 +273 +1 + 9 + +274 +1 +275 +1 +276 +1 + 42 +1.0 + 72 +1 + 3 + + 73 +1 + 74 +1 + 75 +1 + 94 +63 + 0 +MLINESTYLE + 5 +22 +102 +{ACAD_REACTORS +330 +10 +102 +} +330 +10 +100 +AcDbMlineStyle + 2 +Standard + 70 +0 + 3 + + 62 +256 + 51 +90.0 + 52 +90.0 + 71 +2 + 49 +0.5 + 62 +256 + 6 +BYLAYER + 49 +-0.5 + 62 +256 + 6 +BYLAYER + 0 +MLEADERSTYLE + 5 +2C +102 +{ACAD_REACTORS +330 +F +102 +} +330 +F +100 +AcDbMLeaderStyle +179 +2 +170 +2 +171 +1 +172 +0 + 90 +2 + 40 +0.0 + 41 +0.0 +173 +1 + 91 +-1056964608 + 92 +-2 +290 +1 + 42 +2.0 +291 +1 + 43 +8.0 + 3 +Standard + 44 +4.0 +300 + +342 +29 +174 +1 +175 +1 +176 +0 +178 +1 + 93 +-1056964608 + 45 +4.0 +292 +0 +297 +0 + 46 +4.0 + 94 +-1056964608 + 47 +1.0 + 49 +1.0 +140 +1.0 +294 +1 +141 +0.0 +177 +0 +142 +1.0 +295 +0 +296 +0 +143 +3.75 +271 +0 +272 +9 +273 +9 + 0 +DICTIONARY + 5 +2D +330 +A +100 +AcDbDictionary +280 +1 +281 +1 + 3 +CREATED_BY_EZDXF +350 +2E + 3 +WRITTEN_BY_EZDXF +350 +385 + 0 +DICTIONARYVAR + 5 +2E +330 +2D +100 +DictionaryVariables +280 +0 + 1 +1.4.2 @ 2025-06-30T10:46:10.542611+00:00 + 0 +DICTIONARYVAR + 5 +385 +330 +2D +100 +DictionaryVariables +280 +0 + 1 +1.4.2 @ 2025-06-30T10:46:10.554003+00:00 + 0 +ENDSEC + 0 +EOF From dda86ffd8d9d35543b81c2b2ab677915bdeb69b8 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Thu, 31 Jul 2025 13:42:46 +0200 Subject: [PATCH 100/134] Use artificial viscosity for TLSPH --- examples/fsi/fin_2d.jl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/fsi/fin_2d.jl b/examples/fsi/fin_2d.jl index efc1c7cd3e..9589d7a8f7 100644 --- a/examples/fsi/fin_2d.jl +++ b/examples/fsi/fin_2d.jl @@ -211,6 +211,7 @@ solid_system = TotalLagrangianSPHSystem(solid, smoothing_kernel, smoothing_lengt modulus, poisson_ratio; n_fixed_particles, movement=boundary_movement, boundary_model=boundary_model_solid, + viscosity=ArtificialViscosityMonaghan(alpha=0.01), penalty_force=PenaltyForceGanzenmueller(alpha=0.1)) # ========================================================================================== @@ -257,9 +258,9 @@ saving_callback = SolutionSavingCallback(dt=0.01, prefix="") split_dt = 2e-5 split_integration = SplitIntegrationCallback(RDPK3SpFSAL35(), adaptive=false, dt=split_dt, maxiters=10^8) -stepsize_callback = StepsizeCallback(cfl=0.5) +stepsize_callback = StepsizeCallback(cfl=1.0) shifting_callback = ParticleShiftingCallback() -callbacks = CallbackSet(info_callback, saving_callback, shifting_callback, +callbacks = CallbackSet(info_callback, saving_callback, #shifting_callback, split_integration, stepsize_callback) # Use a Runge-Kutta method with automatic (error based) time step size control. From febdbc2d571b894d67fa62420c566f0af285cf9d Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Wed, 3 Sep 2025 15:16:47 +0200 Subject: [PATCH 101/134] Fix fin example --- examples/fsi/fin_2d.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/fsi/fin_2d.jl b/examples/fsi/fin_2d.jl index 9589d7a8f7..069aa16220 100644 --- a/examples/fsi/fin_2d.jl +++ b/examples/fsi/fin_2d.jl @@ -58,9 +58,9 @@ n_particles_per_dimension = (round(Int, (fin_length + length_clamp) / particle_s # Note that the `RectangularShape` puts the first particle half a particle spacing away # from the boundary, which is correct for fluids, but not for solids. -# We therefore need to pass `tlsph=true`. +# We therefore need to pass `place_on_shell=true`. beam = RectangularShape(particle_spacing, n_particles_per_dimension, - (-length_clamp, 0.0), density=density, tlsph=true) + (-length_clamp, 0.0), density=density, place_on_shell=true) fixed_particles = setdiff(shape_sampled, beam) @@ -224,7 +224,7 @@ fluid_system = WeaklyCompressibleSPHSystem(fluid, fluid_density_calculator, state_equation, smoothing_kernel, smoothing_length_fluid, viscosity=viscosity_fluid, density_diffusion=density_diffusion, - particle_shifting=TrixiParticles.ParticleShiftingSun2019(0.1 * sound_speed), + shifting_technique=ParticleShiftingTechnique(), pressure_acceleration=tensile_instability_control) # fluid_system = EntropicallyDampedSPHSystem(fluid, smoothing_kernel, smoothing_length, # sound_speed, viscosity=ViscosityAdami(; nu), From 2411592af8cb1b807bf131723a4cf1edd39a88a6 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Wed, 3 Sep 2025 16:01:01 +0200 Subject: [PATCH 102/134] Fix sound speed --- examples/fsi/fin_2d.jl | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/examples/fsi/fin_2d.jl b/examples/fsi/fin_2d.jl index 069aa16220..6f98b59e98 100644 --- a/examples/fsi/fin_2d.jl +++ b/examples/fsi/fin_2d.jl @@ -180,7 +180,7 @@ boundary_movement = TrixiParticles.oscillating_movement(frequency, rotation_angle, center; rotation_phase_offset, ramp_up=0.5) -sound_speed = 20.0 +sound_speed = 40.0 state_equation = StateEquationCole(; sound_speed, reference_density=fluid_density, exponent=1, background_pressure=0.0) @@ -255,12 +255,11 @@ ode = semidiscretize(semi, tspan) info_callback = InfoCallback(interval=100) saving_callback = SolutionSavingCallback(dt=0.01, prefix="") -split_dt = 2e-5 +split_dt = 1e-5 split_integration = SplitIntegrationCallback(RDPK3SpFSAL35(), adaptive=false, dt=split_dt, maxiters=10^8) stepsize_callback = StepsizeCallback(cfl=1.0) -shifting_callback = ParticleShiftingCallback() -callbacks = CallbackSet(info_callback, saving_callback, #shifting_callback, +callbacks = CallbackSet(info_callback, saving_callback, UpdateCallback(), split_integration, stepsize_callback) # Use a Runge-Kutta method with automatic (error based) time step size control. From d15c504f3fa38b7d391639292a401e93d77ead8f Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Wed, 24 Sep 2025 16:35:37 +0200 Subject: [PATCH 103/134] Update fin example file --- examples/fsi/fin_2d.jl | 48 +++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/examples/fsi/fin_2d.jl b/examples/fsi/fin_2d.jl index 6f98b59e98..806ba76bcd 100644 --- a/examples/fsi/fin_2d.jl +++ b/examples/fsi/fin_2d.jl @@ -34,7 +34,7 @@ initial_velocity = (1.0, 0.0) particle_spacing = fin_thickness / (n_particles_y - 1) fluid_particle_spacing = particle_spacing -smoothing_length_solid = sqrt(2) * particle_spacing +smoothing_length_structure = sqrt(2) * particle_spacing smoothing_length_fluid = 2 * fluid_particle_spacing smoothing_kernel = WendlandC2Kernel{2}() @@ -57,14 +57,14 @@ n_particles_per_dimension = (round(Int, (fin_length + length_clamp) / particle_s n_particles_y) # Note that the `RectangularShape` puts the first particle half a particle spacing away -# from the boundary, which is correct for fluids, but not for solids. +# from the boundary, which is correct for fluids, but not for structures. # We therefore need to pass `place_on_shell=true`. beam = RectangularShape(particle_spacing, n_particles_per_dimension, (-length_clamp, 0.0), density=density, place_on_shell=true) fixed_particles = setdiff(shape_sampled, beam) -# solid = union(beam, fixed_particles) +# structure = union(beam, fixed_particles) # Change spacing ratio to 3 and boundary layers to 1 when using Monaghan-Kajtar boundary model boundary_layers = 4 @@ -73,7 +73,7 @@ fluid_density = 1000.0 tank = RectangularTank(fluid_particle_spacing, initial_fluid_size, tank_size, fluid_density, n_layers=boundary_layers, spacing_ratio=spacing_ratio, faces=(false, false, true, true), velocity=initial_velocity) -# fluid = setdiff(tank.fluid, solid) +# fluid = setdiff(tank.fluid, structure) # ========================================================================================== # ==== Packing @@ -121,10 +121,10 @@ packed_foot = InitialCondition(sol_packing, foot_packing_system, semi_packing) packed_foot.coordinates .+= center beam.coordinates .+= center -solid = union(beam, packed_foot) -fluid = setdiff(tank.fluid, solid) +structure = union(beam, packed_foot) +fluid = setdiff(tank.fluid, structure) -n_fixed_particles = nparticles(solid) - nparticles(beam) +n_clamped_particles = nparticles(structure) - nparticles(beam) # Pack the fluid against the fin and the tank boundary pack_window = TrixiParticles.Polygon(stack([ @@ -144,7 +144,7 @@ pack_window = TrixiParticles.Polygon(stack([ pack_fluid = intersect(fluid, pack_window) # and those outside the window fixed_fluid = setdiff(fluid, pack_fluid) -fixed_union = union(fixed_fluid, solid) +fixed_union = union(fixed_fluid, structure) fluid_packing_system = ParticlePackingSystem(pack_fluid; smoothing_length=smoothing_length_packing, signed_distance_field=nothing, background_pressure) @@ -175,42 +175,42 @@ rotation_phase_offset = 0.12 # periods translation_vector = SVector(0.0, amplitude) rotation_angle = rotation_deg * pi / 180 -boundary_movement = TrixiParticles.oscillating_movement(frequency, - SVector(0.0, amplitude), - rotation_angle, center; - rotation_phase_offset, ramp_up=0.5) +boundary_motion = OscillatingMotion2D(; frequency, + translation_vector=SVector(0.0, amplitude), + rotation_angle, rotation_center=center, + rotation_phase_offset, ramp_up_tspan=(0.0, 0.5)) sound_speed = 40.0 state_equation = StateEquationCole(; sound_speed, reference_density=fluid_density, exponent=1, background_pressure=0.0) # ========================================================================================== -# ==== Solid +# ==== Structure boundary_density_calculator = AdamiPressureExtrapolation() viscosity_fluid = ViscosityAdami(nu=1e-4) viscosity_fin = ViscosityAdami(nu=1e-4) -# For the FSI we need the hydrodynamic masses and densities in the solid boundary model -hydrodynamic_densites = fluid_density * ones(size(solid.density)) +# For the FSI we need the hydrodynamic masses and densities in the structure boundary model +hydrodynamic_densites = fluid_density * ones(size(structure.density)) hydrodynamic_masses = hydrodynamic_densites * particle_spacing^2 -boundary_model_solid = BoundaryModelDummyParticles(hydrodynamic_densites, +boundary_model_structure = BoundaryModelDummyParticles(hydrodynamic_densites, hydrodynamic_masses, state_equation=state_equation, boundary_density_calculator, smoothing_kernel, smoothing_length_fluid, viscosity=viscosity_fin) -# k_solid = 1.0 -# beta_solid = fluid_particle_spacing / particle_spacing -# boundary_model_solid = BoundaryModelMonaghanKajtar(k_solid, beta_solid, +# k_structure = 1.0 +# beta_structure = fluid_particle_spacing / particle_spacing +# boundary_model_structure = BoundaryModelMonaghanKajtar(k_structure, beta_structure, # particle_spacing, # hydrodynamic_masses) -solid_system = TotalLagrangianSPHSystem(solid, smoothing_kernel, smoothing_length_solid, +structure_system = TotalLagrangianSPHSystem(structure, smoothing_kernel, smoothing_length_structure, modulus, poisson_ratio; - n_fixed_particles, movement=boundary_movement, - boundary_model=boundary_model_solid, + n_clamped_particles, clamped_particles_motion=boundary_motion, + boundary_model=boundary_model_structure, viscosity=ArtificialViscosityMonaghan(alpha=0.01), penalty_force=PenaltyForceGanzenmueller(alpha=0.1)) @@ -238,7 +238,7 @@ boundary_model = BoundaryModelDummyParticles(tank.boundary.density, tank.boundar boundary_density_calculator, smoothing_kernel, smoothing_length_fluid) -boundary_system = BoundarySPHSystem(tank.boundary, boundary_model) +boundary_system = WallBoundarySystem(tank.boundary, boundary_model) # ========================================================================================== # ==== Simulation @@ -248,7 +248,7 @@ periodic_box = PeriodicBox(; min_corner, max_corner) cell_list = FullGridCellList(; min_corner, max_corner) neighborhood_search = GridNeighborhoodSearch{2}(; periodic_box, cell_list, update_strategy=ParallelUpdate()) -semi = Semidiscretization(fluid_system, boundary_system, solid_system; neighborhood_search, +semi = Semidiscretization(fluid_system, boundary_system, structure_system; neighborhood_search, parallelization_backend=PolyesterBackend()) ode = semidiscretize(semi, tspan) From ac80422c75a14ac0f8c54df46207263dfa4e2ac8 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Mon, 22 Dec 2025 10:40:55 +0100 Subject: [PATCH 104/134] Add open boundaries to fin simulation --- examples/fsi/fin_2d.jl | 281 ++++++++++++++++++++++++++--------------- 1 file changed, 178 insertions(+), 103 deletions(-) diff --git a/examples/fsi/fin_2d.jl b/examples/fsi/fin_2d.jl index 806ba76bcd..480f8517d9 100644 --- a/examples/fsi/fin_2d.jl +++ b/examples/fsi/fin_2d.jl @@ -4,15 +4,15 @@ using OrdinaryDiffEqLowStorageRK # ========================================================================================== # ==== Resolution -n_particles_y = 6 +n_particles_y = 4 # ========================================================================================== # ==== Experiment Setup -tspan = (0.0, 3.0) +tspan = (0.0, 2.0) fin_length = 0.6 -fin_thickness = 10e-3 -flexural_rigidity = 6.0 +fin_thickness = 30e-3 +flexural_rigidity = 60.0 poisson_ratio = 0.3 modulus = 12 * (1 - poisson_ratio^2) * flexural_rigidity / (fin_thickness^3) @@ -66,107 +66,140 @@ fixed_particles = setdiff(shape_sampled, beam) # structure = union(beam, fixed_particles) -# Change spacing ratio to 3 and boundary layers to 1 when using Monaghan-Kajtar boundary model -boundary_layers = 4 -spacing_ratio = 1 +# Make sure that the kernel support of fluid particles at a boundary is always fully sampled +boundary_layers = 3 + +# Make sure that the kernel support of fluid particles at an open boundary is always +# fully sampled. +# Note: Due to the dynamics at the inlets and outlets of open boundaries, +# it is recommended to use `open_boundary_layers > boundary_layers` +open_boundary_layers = 6 + fluid_density = 1000.0 tank = RectangularTank(fluid_particle_spacing, initial_fluid_size, tank_size, fluid_density, - n_layers=boundary_layers, spacing_ratio=spacing_ratio, + n_layers=boundary_layers, faces=(false, false, true, true), velocity=initial_velocity) # fluid = setdiff(tank.fluid, structure) -# ========================================================================================== -# ==== Packing -foot_sdf = SignedDistanceField(geometry, particle_spacing; - max_signed_distance=4 * particle_spacing, - use_for_boundary_packing=true) - -boundary_packing = sample_boundary(foot_sdf; boundary_density=density, - boundary_thickness=4 * particle_spacing) -boundary_packing = setdiff(boundary_packing, beam) - -background_pressure = 1.0 -smoothing_length_packing = 0.8 * particle_spacing -foot_packing_system = ParticlePackingSystem(fixed_particles; smoothing_length=smoothing_length_packing, - signed_distance_field=foot_sdf, background_pressure) - -fluid_packing_system = ParticlePackingSystem(boundary_packing; smoothing_length=smoothing_length_packing, - signed_distance_field=foot_sdf, is_boundary=true, background_pressure, - boundary_compress_factor=0.8) - -blade_packing_system = ParticlePackingSystem(beam; smoothing_length=smoothing_length_packing, - fixed_system=true, signed_distance_field=nothing, background_pressure) +open_boundary_size = (fluid_particle_spacing * open_boundary_layers, tank_size[2]) -min_corner = minimum(tank.boundary.coordinates, dims=2) .- fluid_particle_spacing / 2 -max_corner = maximum(tank.boundary.coordinates, dims=2) .+ fluid_particle_spacing / 2 -periodic_box = PeriodicBox(; min_corner, max_corner) -cell_list = FullGridCellList(; min_corner, max_corner) -neighborhood_search = GridNeighborhoodSearch{2}(; periodic_box, cell_list, update_strategy=ParallelUpdate()) - -semi_packing = Semidiscretization(foot_packing_system, fluid_packing_system, - blade_packing_system; neighborhood_search) +min_coords_inlet = (-open_boundary_layers * fluid_particle_spacing, 0.0) +inlet = RectangularTank(fluid_particle_spacing, open_boundary_size, open_boundary_size, + fluid_density, n_layers=boundary_layers, + min_coordinates=min_coords_inlet, + faces=(false, false, true, true)) -ode_packing = semidiscretize(semi_packing, (0.0, 10.0)) +min_coords_outlet = (tank.fluid_size[1], 0.0) +outlet = RectangularTank(fluid_particle_spacing, open_boundary_size, open_boundary_size, + fluid_density, n_layers=boundary_layers, + min_coordinates=min_coords_outlet, + faces=(false, false, true, true)) -sol_packing = solve(ode_packing, RDPK3SpFSAL35(); - save_everystep=false, - callback=CallbackSet(InfoCallback(interval=50), - # SolutionSavingCallback(interval=50, prefix="packing"), - UpdateCallback()), - dtmax=1e-2) -packed_foot = InitialCondition(sol_packing, foot_packing_system, semi_packing) +NDIMS = ndims(tank.fluid) +n_buffer_particles = 10 * tank.n_particles_per_dimension[2]^(NDIMS - 1) -# Move the fin to the center of the tank -packed_foot.coordinates .+= center -beam.coordinates .+= center - -structure = union(beam, packed_foot) -fluid = setdiff(tank.fluid, structure) +# ========================================================================================== +# ==== Packing +packing = false +if packing + foot_sdf = SignedDistanceField(geometry, particle_spacing; + max_signed_distance=4 * particle_spacing, + use_for_boundary_packing=true) + + boundary_packing = sample_boundary(foot_sdf; boundary_density=density, + boundary_thickness=4 * particle_spacing) + boundary_packing = setdiff(boundary_packing, beam) + + background_pressure = 1.0 + smoothing_length_packing = 0.8 * particle_spacing + foot_packing_system = ParticlePackingSystem(fixed_particles; smoothing_length=smoothing_length_packing, + signed_distance_field=foot_sdf, background_pressure) + + fluid_packing_system = ParticlePackingSystem(boundary_packing; smoothing_length=smoothing_length_packing, + signed_distance_field=foot_sdf, is_boundary=true, background_pressure, + boundary_compress_factor=0.8) + + blade_packing_system = ParticlePackingSystem(beam; smoothing_length=smoothing_length_packing, + fixed_system=true, signed_distance_field=nothing, background_pressure) + + min_corner = minimum(tank.boundary.coordinates, dims=2) .- fluid_particle_spacing / 2 + max_corner = maximum(tank.boundary.coordinates, dims=2) .+ fluid_particle_spacing / 2 + periodic_box = PeriodicBox(; min_corner, max_corner) + cell_list = FullGridCellList(; min_corner, max_corner) + neighborhood_search = GridNeighborhoodSearch{2}(; periodic_box, cell_list, update_strategy=ParallelUpdate()) + + semi_packing = Semidiscretization(foot_packing_system, fluid_packing_system, + blade_packing_system; neighborhood_search) + + ode_packing = semidiscretize(semi_packing, (0.0, 10.0)) + + sol_packing = solve(ode_packing, RDPK3SpFSAL35(); + save_everystep=false, + callback=CallbackSet(InfoCallback(interval=50), + # SolutionSavingCallback(interval=50, prefix="packing"), + UpdateCallback()), + dtmax=1e-2) + + packed_foot = InitialCondition(sol_packing, foot_packing_system, semi_packing) + + # Move the fin to the center of the tank + packed_foot.coordinates .+= center + beam.coordinates .+= center + + structure = union(beam, packed_foot) + fluid = setdiff(tank.fluid, structure) + + # Pack the fluid against the fin and the tank boundary + pack_window = TrixiParticles.Polygon(stack([ + [0.15, 0.42], + [0.3, 0.42], + [0.44, 0.48], + [1.12, 0.48], + [1.12, 0.52], + [0.55, 0.52], + [0.5, 0.56], + [0.24, 0.6], + [0.15, 0.6], + [0.15, 0.42] + ])) + + # Then, we extract the particles that fall inside this window + pack_fluid = intersect(fluid, pack_window) + # and those outside the window + fixed_fluid = setdiff(fluid, pack_fluid) + fixed_union = union(fixed_fluid, structure) + + fluid_packing_system = ParticlePackingSystem(pack_fluid; smoothing_length=smoothing_length_packing, + signed_distance_field=nothing, background_pressure) + + fixed_packing_system = ParticlePackingSystem(fixed_union; smoothing_length=smoothing_length_packing, + fixed_system=true, signed_distance_field=nothing, background_pressure) + + semi_packing = Semidiscretization(fluid_packing_system, fixed_packing_system; + neighborhood_search) + + ode_packing = semidiscretize(semi_packing, (0.0, 2.0)) + + sol_packing = solve(ode_packing, RDPK3SpFSAL35(); + save_everystep=false, + callback=CallbackSet(InfoCallback(interval=50), + # SolutionSavingCallback(interval=50, prefix="packing"), + UpdateCallback()), + dtmax=1e-2) + + fluid = InitialCondition(sol_packing, fluid_packing_system, semi_packing) + fluid = union(fluid, fixed_fluid) +else + structure = union(beam, fixed_particles) + # Move the fin to the center of the tank + structure.coordinates .+= center + + fluid = setdiff(tank.fluid, structure) +end n_clamped_particles = nparticles(structure) - nparticles(beam) -# Pack the fluid against the fin and the tank boundary -pack_window = TrixiParticles.Polygon(stack([ - [0.15, 0.42], - [0.3, 0.42], - [0.44, 0.48], - [1.12, 0.48], - [1.12, 0.52], - [0.55, 0.52], - [0.5, 0.56], - [0.24, 0.6], - [0.15, 0.6], - [0.15, 0.42] - ])) - -# Then, we extract the particles that fall inside this window -pack_fluid = intersect(fluid, pack_window) -# and those outside the window -fixed_fluid = setdiff(fluid, pack_fluid) -fixed_union = union(fixed_fluid, structure) - -fluid_packing_system = ParticlePackingSystem(pack_fluid; smoothing_length=smoothing_length_packing, - signed_distance_field=nothing, background_pressure) - -fixed_packing_system = ParticlePackingSystem(fixed_union; smoothing_length=smoothing_length_packing, - fixed_system=true, signed_distance_field=nothing, background_pressure) - -semi_packing = Semidiscretization(fluid_packing_system, fixed_packing_system; - neighborhood_search) - -ode_packing = semidiscretize(semi_packing, (0.0, 2.0)) - -sol_packing = solve(ode_packing, RDPK3SpFSAL35(); - save_everystep=false, - callback=CallbackSet(InfoCallback(interval=50), - # SolutionSavingCallback(interval=50, prefix="packing"), - UpdateCallback()), - dtmax=1e-2) - -fluid = InitialCondition(sol_packing, fluid_packing_system, semi_packing) -fluid = union(fluid, fixed_fluid) - # Movement function frequency = 1.3 # Hz amplitude = 0.18 # m @@ -224,38 +257,77 @@ fluid_system = WeaklyCompressibleSPHSystem(fluid, fluid_density_calculator, state_equation, smoothing_kernel, smoothing_length_fluid, viscosity=viscosity_fluid, density_diffusion=density_diffusion, - shifting_technique=ParticleShiftingTechnique(), - pressure_acceleration=tensile_instability_control) + shifting_technique=ParticleShiftingTechnique(sound_speed_factor=0.2, v_max_factor=0.0), + pressure_acceleration=tensile_instability_control, + buffer_size=n_buffer_particles) # fluid_system = EntropicallyDampedSPHSystem(fluid, smoothing_kernel, smoothing_length, # sound_speed, viscosity=ViscosityAdami(; nu), # transport_velocity=TransportVelocityAdami(10 * sound_speed^2 * fluid_density)) +# ========================================================================================== +# ==== Open Boundaries +function velocity_function2d(pos, t) + return SVector(1.0, 0.0) +end + +open_boundary_model = BoundaryModelDynamicalPressureZhang() +# open_boundary_model = BoundaryModelMirroringTafuni(; mirror_method=ZerothOrderMirroring()) +reference_velocity_in = velocity_function2d +reference_pressure_in = nothing +reference_density_in = nothing +boundary_type_in = InFlow() +face_in = ([0.0, 0.0], [0.0, tank_size[2]]) +flow_direction = [1.0, 0.0] +inflow = BoundaryZone(; boundary_face=face_in, face_normal=flow_direction, + open_boundary_layers, density=fluid_density, particle_spacing, + reference_density=reference_density_in, + reference_pressure=reference_pressure_in, + reference_velocity=reference_velocity_in, + initial_condition=inlet.fluid, boundary_type=boundary_type_in) + +reference_velocity_out = SVector(1.0, 0.0) +reference_pressure_out = nothing +reference_density_out = nothing +boundary_type_out = OutFlow() +face_out = ([min_coords_outlet[1], 0.0], [min_coords_outlet[1], tank_size[2]]) +outflow = BoundaryZone(; boundary_face=face_out, face_normal=(-flow_direction), + open_boundary_layers, density=fluid_density, particle_spacing, + reference_density=reference_density_out, + reference_pressure=reference_pressure_out, + reference_velocity=reference_velocity_out, + initial_condition=outlet.fluid, boundary_type=boundary_type_out) + +open_boundary_system = OpenBoundarySystem(inflow, outflow; fluid_system, + boundary_model=open_boundary_model, + buffer_size=n_buffer_particles, + shifting_technique=nothing) + # ========================================================================================== # ==== Boundary +wall = union(tank.boundary, inlet.boundary, outlet.boundary) boundary_density_calculator = AdamiPressureExtrapolation() -boundary_model = BoundaryModelDummyParticles(tank.boundary.density, tank.boundary.mass, +boundary_model = BoundaryModelDummyParticles(wall.density, wall.mass, state_equation=state_equation, boundary_density_calculator, smoothing_kernel, smoothing_length_fluid) -boundary_system = WallBoundarySystem(tank.boundary, boundary_model) +boundary_system = WallBoundarySystem(wall, boundary_model) # ========================================================================================== # ==== Simulation -min_corner = minimum(tank.boundary.coordinates, dims=2) .- fluid_particle_spacing / 2 -max_corner = maximum(tank.boundary.coordinates, dims=2) .+ fluid_particle_spacing / 2 -periodic_box = PeriodicBox(; min_corner, max_corner) +min_corner = minimum(wall.coordinates, dims=2) .- fluid_particle_spacing / 2 +max_corner = maximum(wall.coordinates, dims=2) .+ fluid_particle_spacing / 2 cell_list = FullGridCellList(; min_corner, max_corner) -neighborhood_search = GridNeighborhoodSearch{2}(; periodic_box, cell_list, update_strategy=ParallelUpdate()) +neighborhood_search = GridNeighborhoodSearch{2}(; cell_list, update_strategy=ParallelUpdate()) -semi = Semidiscretization(fluid_system, boundary_system, structure_system; neighborhood_search, +semi = Semidiscretization(fluid_system, boundary_system, open_boundary_system, structure_system; neighborhood_search, parallelization_backend=PolyesterBackend()) ode = semidiscretize(semi, tspan) info_callback = InfoCallback(interval=100) saving_callback = SolutionSavingCallback(dt=0.01, prefix="") -split_dt = 1e-5 +split_dt = 5e-5 split_integration = SplitIntegrationCallback(RDPK3SpFSAL35(), adaptive=false, dt=split_dt, maxiters=10^8) stepsize_callback = StepsizeCallback(cfl=1.0) @@ -276,6 +348,9 @@ callbacks = CallbackSet(info_callback, saving_callback, UpdateCallback(), # save_everystep=false, callback=callbacks, maxiters=10^8); dt_fluid = 1.25e-4 -sol = solve(ode, CarpenterKennedy2N54(williamson_condition=false), +sol = solve(ode, + # RDPK3SpFSAL35(), + CarpenterKennedy2N54(williamson_condition=false), dt=dt_fluid, # This is overwritten by the stepsize callback + # reltol=1e-5, abstol=1e-7, save_everystep=false, callback=callbacks, maxiters=10^8); From 034ddd0c9881fcee7ade45c7e70bfcbcddb87f84 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Wed, 24 Dec 2025 11:37:09 +0100 Subject: [PATCH 105/134] Optimize time integration and add periodic option --- examples/fsi/fin_2d.jl | 125 +++++++++++++++++++++++------------------ 1 file changed, 70 insertions(+), 55 deletions(-) diff --git a/examples/fsi/fin_2d.jl b/examples/fsi/fin_2d.jl index 480f8517d9..f8aa73924c 100644 --- a/examples/fsi/fin_2d.jl +++ b/examples/fsi/fin_2d.jl @@ -1,6 +1,6 @@ using TrixiParticles using OrdinaryDiffEqLowStorageRK -# using OrdinaryDiffEqSymplecticRK +using OrdinaryDiffEqSymplecticRK # ========================================================================================== # ==== Resolution @@ -12,9 +12,11 @@ tspan = (0.0, 2.0) fin_length = 0.6 fin_thickness = 30e-3 -flexural_rigidity = 60.0 +real_thickness = 1e-3 +real_modulus = 125e9 poisson_ratio = 0.3 -modulus = 12 * (1 - poisson_ratio^2) * flexural_rigidity / (fin_thickness^3) +flexural_rigidity = real_modulus * real_thickness^3 / (1 - poisson_ratio^2) / 12 +modulus = 12 * (1 - poisson_ratio^2) * flexural_rigidity / fin_thickness^3 fiber_volume_fraction = 0.6 fiber_density = 1800.0 @@ -266,45 +268,59 @@ fluid_system = WeaklyCompressibleSPHSystem(fluid, fluid_density_calculator, # ========================================================================================== # ==== Open Boundaries -function velocity_function2d(pos, t) - return SVector(1.0, 0.0) +periodic = false +if periodic + min_corner = minimum(tank.boundary.coordinates, dims=2) .- fluid_particle_spacing / 2 + max_corner = maximum(tank.boundary.coordinates, dims=2) .+ fluid_particle_spacing / 2 + min_corner = convert.(typeof(fluid_particle_spacing), min_corner) + max_corner = convert.(typeof(fluid_particle_spacing), max_corner) + periodic_box = PeriodicBox(; min_corner, max_corner) + open_boundary_system = nothing + wall = tank.boundary +else + periodic_box = nothing + + function velocity_function2d(pos, t) + return SVector(1.0, 0.0) + end + + open_boundary_model = BoundaryModelDynamicalPressureZhang() + # open_boundary_model = BoundaryModelMirroringTafuni(; mirror_method=ZerothOrderMirroring()) + reference_velocity_in = velocity_function2d + reference_pressure_in = nothing + reference_density_in = nothing + boundary_type_in = InFlow() + face_in = ([0.0, 0.0], [0.0, tank_size[2]]) + flow_direction = [1.0, 0.0] + inflow = BoundaryZone(; boundary_face=face_in, face_normal=flow_direction, + open_boundary_layers, density=fluid_density, particle_spacing, + reference_density=reference_density_in, + reference_pressure=reference_pressure_in, + reference_velocity=reference_velocity_in, + initial_condition=inlet.fluid, boundary_type=boundary_type_in) + + reference_velocity_out = SVector(1.0, 0.0) + reference_pressure_out = nothing + reference_density_out = nothing + boundary_type_out = OutFlow() + face_out = ([min_coords_outlet[1], 0.0], [min_coords_outlet[1], tank_size[2]]) + outflow = BoundaryZone(; boundary_face=face_out, face_normal=(-flow_direction), + open_boundary_layers, density=fluid_density, particle_spacing, + reference_density=reference_density_out, + reference_pressure=reference_pressure_out, + reference_velocity=reference_velocity_out, + initial_condition=outlet.fluid, boundary_type=boundary_type_out) + + open_boundary_system = OpenBoundarySystem(inflow, outflow; fluid_system, + boundary_model=open_boundary_model, + buffer_size=n_buffer_particles, + shifting_technique=nothing) + + wall = union(tank.boundary, inlet.boundary, outlet.boundary) end -open_boundary_model = BoundaryModelDynamicalPressureZhang() -# open_boundary_model = BoundaryModelMirroringTafuni(; mirror_method=ZerothOrderMirroring()) -reference_velocity_in = velocity_function2d -reference_pressure_in = nothing -reference_density_in = nothing -boundary_type_in = InFlow() -face_in = ([0.0, 0.0], [0.0, tank_size[2]]) -flow_direction = [1.0, 0.0] -inflow = BoundaryZone(; boundary_face=face_in, face_normal=flow_direction, - open_boundary_layers, density=fluid_density, particle_spacing, - reference_density=reference_density_in, - reference_pressure=reference_pressure_in, - reference_velocity=reference_velocity_in, - initial_condition=inlet.fluid, boundary_type=boundary_type_in) - -reference_velocity_out = SVector(1.0, 0.0) -reference_pressure_out = nothing -reference_density_out = nothing -boundary_type_out = OutFlow() -face_out = ([min_coords_outlet[1], 0.0], [min_coords_outlet[1], tank_size[2]]) -outflow = BoundaryZone(; boundary_face=face_out, face_normal=(-flow_direction), - open_boundary_layers, density=fluid_density, particle_spacing, - reference_density=reference_density_out, - reference_pressure=reference_pressure_out, - reference_velocity=reference_velocity_out, - initial_condition=outlet.fluid, boundary_type=boundary_type_out) - -open_boundary_system = OpenBoundarySystem(inflow, outflow; fluid_system, - boundary_model=open_boundary_model, - buffer_size=n_buffer_particles, - shifting_technique=nothing) - # ========================================================================================== # ==== Boundary -wall = union(tank.boundary, inlet.boundary, outlet.boundary) boundary_density_calculator = AdamiPressureExtrapolation() boundary_model = BoundaryModelDummyParticles(wall.density, wall.mass, state_equation=state_equation, @@ -318,7 +334,8 @@ boundary_system = WallBoundarySystem(wall, boundary_model) min_corner = minimum(wall.coordinates, dims=2) .- fluid_particle_spacing / 2 max_corner = maximum(wall.coordinates, dims=2) .+ fluid_particle_spacing / 2 cell_list = FullGridCellList(; min_corner, max_corner) -neighborhood_search = GridNeighborhoodSearch{2}(; cell_list, update_strategy=ParallelUpdate()) +neighborhood_search = GridNeighborhoodSearch{2}(; periodic_box, cell_list, + update_strategy=ParallelUpdate()) semi = Semidiscretization(fluid_system, boundary_system, open_boundary_system, structure_system; neighborhood_search, parallelization_backend=PolyesterBackend()) @@ -327,26 +344,24 @@ ode = semidiscretize(semi, tspan) info_callback = InfoCallback(interval=100) saving_callback = SolutionSavingCallback(dt=0.01, prefix="") -split_dt = 5e-5 -split_integration = SplitIntegrationCallback(RDPK3SpFSAL35(), adaptive=false, dt=split_dt, +split_cfl = 0.5 +# SSPRK104 CFL = 2.5, 15k RHS evaluations +# CarpenterKennedy2N54 CFL = 1.6, 11k RHS evaluations +# RK4 CFL = 1.2, 12k RHS evaluations +# VerletLeapfrog CFL = 0.5, 6.75k RHS evaluations +# VelocityVerlet CFL = 0.5, 6.75k RHS evaluations +# DPRKN4 CFL = 1.7, 9k RHS evaluations + +split_integration = SplitIntegrationCallback(VerletLeapfrog(), adaptive=false, + dt=1e-5, # This is overwritten by the stepsize callback + callback=StepsizeCallback(cfl=split_cfl), maxiters=10^8) -stepsize_callback = StepsizeCallback(cfl=1.0) + +fluid_cfl = 0.4 +stepsize_callback = StepsizeCallback(cfl=fluid_cfl) callbacks = CallbackSet(info_callback, saving_callback, UpdateCallback(), split_integration, stepsize_callback) -# Use a Runge-Kutta method with automatic (error based) time step size control. -# Limiting of the maximum stepsize is necessary to prevent crashing. -# When particles are approaching a wall in a uniform way, they can be advanced -# with large time steps. Close to the wall, the stepsize has to be reduced drastically. -# Sometimes, the method fails to do so because forces become extremely large when -# fluid particles are very close to boundary particles, and the time integration method -# interprets this as an instability. -# sol = solve(ode, RDPK3SpFSAL35(), -# abstol=1e-8, # Default abstol is 1e-6 (may need to be tuned to prevent boundary penetration) -# reltol=1e-6, # Default reltol is 1e-3 (may need to be tuned to prevent boundary penetration) -# dtmax=1e-2, # Limit stepsize to prevent crashing -# save_everystep=false, callback=callbacks, maxiters=10^8); - dt_fluid = 1.25e-4 sol = solve(ode, # RDPK3SpFSAL35(), From 73fdb848ad01b0f208390a080522a43dc7482a79 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Wed, 21 Jan 2026 11:48:28 +0100 Subject: [PATCH 106/134] Fix foot rotation --- examples/fsi/fin_2d.jl | 23 +- examples/preprocessing/data/fin.dxf | 16080 +------------------------- 2 files changed, 548 insertions(+), 15555 deletions(-) diff --git a/examples/fsi/fin_2d.jl b/examples/fsi/fin_2d.jl index f8aa73924c..f5f74fd7cc 100644 --- a/examples/fsi/fin_2d.jl +++ b/examples/fsi/fin_2d.jl @@ -37,7 +37,7 @@ particle_spacing = fin_thickness / (n_particles_y - 1) fluid_particle_spacing = particle_spacing smoothing_length_structure = sqrt(2) * particle_spacing -smoothing_length_fluid = 2 * fluid_particle_spacing +smoothing_length_fluid = 1.5 * fluid_particle_spacing smoothing_kernel = WendlandC2Kernel{2}() file = joinpath(examples_dir(), "preprocessing", "data", "fin.dxf") @@ -246,7 +246,7 @@ structure_system = TotalLagrangianSPHSystem(structure, smoothing_kernel, smoothi modulus, poisson_ratio; n_clamped_particles, clamped_particles_motion=boundary_motion, boundary_model=boundary_model_structure, - viscosity=ArtificialViscosityMonaghan(alpha=0.01), + viscosity=ArtificialViscosityMonaghan(alpha=0.1), penalty_force=PenaltyForceGanzenmueller(alpha=0.1)) # ========================================================================================== @@ -344,7 +344,7 @@ ode = semidiscretize(semi, tspan) info_callback = InfoCallback(interval=100) saving_callback = SolutionSavingCallback(dt=0.01, prefix="") -split_cfl = 0.5 +split_cfl = 1.0 # SSPRK104 CFL = 2.5, 15k RHS evaluations # CarpenterKennedy2N54 CFL = 1.6, 11k RHS evaluations # RK4 CFL = 1.2, 12k RHS evaluations @@ -352,15 +352,26 @@ split_cfl = 0.5 # VelocityVerlet CFL = 0.5, 6.75k RHS evaluations # DPRKN4 CFL = 1.7, 9k RHS evaluations -split_integration = SplitIntegrationCallback(VerletLeapfrog(), adaptive=false, +split_integration = SplitIntegrationCallback(CarpenterKennedy2N54(williamson_condition=false), adaptive=false, + stage_coupling=true, dt=1e-5, # This is overwritten by the stepsize callback callback=StepsizeCallback(cfl=split_cfl), maxiters=10^8) -fluid_cfl = 0.4 +fluid_cfl = 0.8 stepsize_callback = StepsizeCallback(cfl=fluid_cfl) + +function total_volume(system::WeaklyCompressibleSPHSystem, data, t) + return sum(data.mass ./ data.density) +end +function total_volume(system, data, t) + return nothing +end +pp_cb = PostprocessCallback(; total_volume, interval=100, + filename="total_volume", write_file_interval=50) + callbacks = CallbackSet(info_callback, saving_callback, UpdateCallback(), - split_integration, stepsize_callback) + stepsize_callback, split_integration, pp_cb) dt_fluid = 1.25e-4 sol = solve(ode, diff --git a/examples/preprocessing/data/fin.dxf b/examples/preprocessing/data/fin.dxf index b69ee51b49..3f56143d5a 100644 --- a/examples/preprocessing/data/fin.dxf +++ b/examples/preprocessing/data/fin.dxf @@ -29,19 +29,19 @@ $INSBASE 9 $EXTMIN 10 -1e+20 +-100 20 -1e+20 +-100 30 -1e+20 +-100 9 $EXTMAX 10 --1e+20 +100 20 --1e+20 +100 30 --1e+20 +100 9 $LIMMIN 10 @@ -489,7 +489,7 @@ $SKPOLY 9 $TDCREATE 40 -2460857.532060185 +2461046.8383217594 9 $TDUCREATE 40 @@ -497,7 +497,7 @@ $TDUCREATE 9 $TDUPDATE 40 -2460857.532060185 +2461046.8383217594 9 $TDUUPDATE 40 @@ -549,7 +549,7 @@ $SPLINESEGS 9 $HANDSEED 5 -386 +DA 9 $SURFTAB1 70 @@ -929,11 +929,11 @@ $PSTYLEMODE 9 $FINGERPRINTGUID 2 -{FAAE200F-94FE-46F6-80FD-E3F6FFB18616} +BD1C87EE-EA69-11F0-BD04-A088C2115B2C 9 $VERSIONGUID 2 -{604F0C71-16BC-45AB-9B96-D28D4B8C28BA} +BD1D174A-EA69-11F0-BD04-A088C2115B2C 9 $EXTNAMES 290 @@ -961,7 +961,7 @@ $HIDETEXT 9 $XCLIPFRAME 280 -1 +2 9 $HALOGAP 280 @@ -1393,9 +1393,9 @@ AcDbViewportTableRecord 21 1.0 12 -0.0 +70.0 22 -0.0 +50.0 13 0.0 23 @@ -1421,7 +1421,7 @@ AcDbViewportTableRecord 37 0.0 40 -1000.0 +1.0 41 1.34 42 @@ -1683,7 +1683,7 @@ APPID 100 AcDbSymbolTable 70 -3 +2 0 APPID 5 @@ -1701,7 +1701,7 @@ ACAD 0 APPID 5 -383 +D9 330 3 100 @@ -1713,20 +1713,6 @@ HATCHBACKGROUNDCOLOR 70 0 0 -APPID - 5 -384 -330 -3 -100 -AcDbSymbolTableRecord -100 -AcDbRegAppTableRecord - 2 -EZDXF - 70 -0 - 0 ENDTAB 0 TABLE @@ -1815,7 +1801,7 @@ Standard 78 8 79 -3 +0 170 0 171 @@ -1835,9 +1821,9 @@ Standard 178 0 179 -2 +0 271 -2 +0 272 2 273 @@ -2025,7 +2011,7 @@ ENTITIES 0 POLYLINE 5 -2F +2D 330 17 100 @@ -2047,7 +2033,29 @@ AcDb2dPolyline 0 VERTEX 5 -31 +2F +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +0.0 + 20 +5.551115123125783e-17 + 30 +0.0 + 70 +0 + 0 +VERTEX + 5 +30 330 17 100 @@ -2059,9 +2067,31 @@ AcDbVertex 100 AcDb2dVertex 10 +-0.0010647730582502013 + 20 +-2.8770736736127844e-05 + 30 0.0 + 70 +0 + 0 +VERTEX + 5 +31 +330 +17 +100 +AcDbEntity + 8 +0 +100 +AcDbVertex +100 +AcDb2dVertex + 10 +-0.0034971934090743395 20 --0.00010000000000000286 +-4.953574980059994e-05 30 0.0 70 @@ -2081,9 +2111,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.00017528351092088723 +-0.007027977637432459 20 -0.0009440195898606021 +-6.326524879191053e-05 30 0.0 70 @@ -2103,9 +2133,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.00036359447194017624 +-0.011387842328284381 20 -0.0019417239071807327 +-7.092944330866491e-05 30 0.0 70 @@ -2125,9 +2155,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.0005649341828909971 +-0.01630750406658993 20 -0.0028931130819437045 +-7.34985429493018e-05 30 0.0 70 @@ -2147,9 +2177,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.0007793039436066462 +-0.02151767943730898 20 -0.0037981872441328474 +-7.19427573124265e-05 30 0.0 70 @@ -2169,9 +2199,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.0010067050539201983 +-0.026749085025401576 20 -0.004656946523731456 +-6.723229599658875e-05 30 0.0 70 @@ -2191,9 +2221,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.0012471388136648387 +-0.031732437415827486 20 -0.005469391050722874 +-6.0337368600282826e-05 30 0.0 70 @@ -2213,9 +2243,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.0015006065226738086 +-0.03619845319354664 20 -0.006235520955090397 +-5.222818472205848e-05 30 0.0 70 @@ -2235,9 +2265,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.0017671094807802934 +-0.03987784894351887 20 -0.006955336366817361 +-4.387495396040997e-05 30 0.0 70 @@ -2257,9 +2287,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.0020466489878173677 +-0.04378446456114665 20 -0.007628837415887074 +-0.0008783586002438781 30 0.0 70 @@ -2279,9 +2309,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.002339226343618328 +-0.048103522430103185 20 -0.008256024232282853 +-0.0025049262890193824 30 0.0 70 @@ -2301,9 +2331,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.002644842848016249 +-0.051718721680916624 20 -0.008836896945988028 +-0.003948651160244676 30 0.0 70 @@ -2323,9 +2353,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.002963499800844427 +-0.0549385925607315 20 -0.009371455686985886 +-0.0052322536218777915 30 0.0 70 @@ -2345,9 +2375,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.0032951985019359364 +-0.05807166531669217 20 -0.009859700585259784 +-0.006378454081876428 30 0.0 70 @@ -2367,9 +2397,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.003636567674683844 +-0.061532460806013545 20 -0.010297617810168594 +-0.007673859629111657 30 0.0 70 @@ -2389,9 +2419,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.003944789489121747 +-0.06516986230184571 20 -0.01065139405731566 +-0.009171395348674838 30 0.0 70 @@ -2411,9 +2441,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.004319217536481679 +-0.06880726379767793 20 -0.01105617700305394 +-0.010668931068237963 30 0.0 70 @@ -2433,9 +2463,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.004754609892778094 +-0.07244466529351018 20 -0.011507421034458724 +-0.0121664667878012 30 0.0 70 @@ -2455,9 +2485,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.005245724634025284 +-0.07608206678934243 20 -0.01200058053860531 +-0.013664002507364326 30 0.0 70 @@ -2477,9 +2507,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.005787319836237814 +-0.0797194682851746 20 -0.012531109902568963 +-0.015161538226927451 30 0.0 70 @@ -2499,9 +2529,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.006374153575429975 +-0.08335686978100681 20 -0.013094463513424998 +-0.016659073946490688 30 0.0 70 @@ -2521,9 +2551,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.007000983927616222 +-0.08697507316482567 20 -0.013686095758248695 +-0.01820113484491409 30 0.0 70 @@ -2543,9 +2573,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.007662568968811012 +-0.09056097837690974 20 -0.014301461024115336 +-0.019818103206756144 30 0.0 70 @@ -2565,9 +2595,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.008353666775028745 +-0.09414688358899384 20 -0.014936013698100215 +-0.02143507156859814 30 0.0 70 @@ -2587,9 +2617,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.009069035422283767 +-0.09773278880107797 20 -0.015585208167278618 +-0.023052039930440138 30 0.0 70 @@ -2609,9 +2639,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.009803432986590588 +-0.1013186940131621 20 -0.01624449881872584 +-0.02466900829228219 30 0.0 70 @@ -2631,9 +2661,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.01055161754396361 +-0.10490459922524614 20 -0.016909340039517162 +-0.026285976654124188 30 0.0 70 @@ -2653,9 +2683,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.011308347170417177 +-0.10849050443733027 20 -0.01757518621672788 +-0.027902945015966185 30 0.0 70 @@ -2675,9 +2705,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.012068379941965801 +-0.1120764096494144 20 -0.018237491737433276 +-0.029519913377808238 30 0.0 70 @@ -2697,9 +2727,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.012826473934623828 +-0.11566231486149847 20 -0.018891710988708656 +-0.031136881739650235 30 0.0 70 @@ -2719,9 +2749,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.013577387224405713 +-0.11924822007358254 20 -0.019533298357629285 +-0.03275385010149223 30 0.0 70 @@ -2741,9 +2771,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.014315877887325856 +-0.12283412528566659 20 -0.020157708231270464 +-0.03437081846333434 30 0.0 70 @@ -2763,9 +2793,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.01503670399939866 +-0.12642003049775075 20 -0.02076039499670749 +-0.03598778682517634 30 0.0 70 @@ -2785,9 +2815,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.01573462363663858 +-0.1300059357098348 20 -0.02133681304101563 +-0.037604755187018335 30 0.0 70 @@ -2807,9 +2837,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.016404394875059958 +-0.1335918409219189 20 -0.021882416751270194 +-0.03922172354886039 30 0.0 70 @@ -2829,9 +2859,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.01704077579067731 +-0.13717774613400305 20 -0.022392660514546456 +-0.040838691910702385 30 0.0 70 @@ -2851,9 +2881,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.01763852445950498 +-0.14372856522610838 20 -0.022862998717919712 +-0.04327496838266798 30 0.0 70 @@ -2873,9 +2903,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.018192398957557365 +-0.14995755911305256 20 -0.023288885748465257 +-0.04558405899092027 30 0.0 70 @@ -2895,9 +2925,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.018697157360848926 +-0.15582399389417462 20 -0.02366577599325837 +-0.04775445959307795 30 0.0 70 @@ -2917,9 +2947,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.019147557745394117 +-0.16132787111901833 20 -0.023989123839374342 +-0.04978616888891935 30 0.0 70 @@ -2939,9 +2969,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.01953835818720734 +-0.16646919233712754 20 -0.024254383673888463 +-0.05167918557822265 30 0.0 70 @@ -2961,9 +2991,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.01986431676230288 +-0.17124795909804597 20 -0.02445700988387603 +-0.053433508360766435 30 0.0 70 @@ -2983,9 +3013,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.020254357686325997 +-0.1756641729513177 20 -0.024672500558981875 +-0.05504913593632882 30 0.0 70 @@ -3005,9 +3035,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.02073676397497981 +-0.17971783544648637 20 -0.024922074604494165 +-0.05652606700468815 30 0.0 70 @@ -3027,9 +3057,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.021295655898442345 +-0.18340894813309583 20 -0.025197768780830576 +-0.05786430026562289 30 0.0 70 @@ -3049,9 +3079,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.02192309920388963 +-0.18673751256069004 20 -0.025496116812679395 +-0.05906383441891133 30 0.0 70 @@ -3071,9 +3101,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.022611159638497857 +-0.18970353027881282 20 -0.02581365242472891 +-0.06012466816433182 30 0.0 70 @@ -3093,9 +3123,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.023351902949443004 +-0.1923070028370079 20 -0.026146909341667413 +-0.06104680020166264 30 0.0 70 @@ -3115,9 +3145,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.02413739488390121 +-0.19454793178481922 20 -0.02649242128818318 +-0.0618302292306821 30 0.0 70 @@ -3137,9 +3167,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.024959701189048444 +-0.19642631867179056 20 -0.026846721988964505 +-0.06247495395116859 30 0.0 70 @@ -3159,9 +3189,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.025810887612060962 +-0.1979421650474658 20 -0.027206345168699665 +-0.06298097306290046 30 0.0 70 @@ -3181,9 +3211,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.02668301990011468 +-0.1990954724613888 20 -0.02756782455207695 +-0.063348285265656 30 0.0 70 @@ -3203,9 +3233,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.027568163800385737 +-0.2000151253740754 20 -0.027927693863784648 +-0.06361126741164846 30 0.0 70 @@ -3225,9 +3255,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.028458385060050218 +-0.20276874829217467 20 -0.028282486828511044 +-0.06435484737989106 30 0.0 70 @@ -3247,9 +3277,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.02934574942628415 +-0.205246815645514 20 -0.02862873717094442 +-0.06479549956536129 30 0.0 70 @@ -3269,9 +3299,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.030222322646263677 +-0.20836029970179235 20 -0.028962978615773072 +-0.06480866119589457 30 0.0 70 @@ -3291,9 +3321,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.031080170467164825 +-0.21302017272870882 20 -0.02928174488768527 +-0.06426976949932639 30 0.0 70 @@ -3313,9 +3343,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.03191135863616368 +-0.21934980512311386 20 -0.029581569711369316 +-0.06318850973792922 30 0.0 70 @@ -3335,9 +3365,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.03270795290043632 +-0.2223415227029405 20 -0.029858986811513482 +-0.062085340964722235 30 0.0 70 @@ -3357,9 +3387,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.03346201900715884 +-0.22495973231109506 20 -0.03011052991280606 +-0.060292609672437714 30 0.0 70 @@ -3379,9 +3409,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.03416562270350726 +-0.2273012549555563 20 -0.030332732739935336 +-0.057927912877090926 30 0.0 70 @@ -3401,9 +3431,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.03481082973665772 +-0.22946291164430282 20 -0.0305221290175896 +-0.05510884759469742 30 0.0 70 @@ -3423,9 +3453,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.03538970585378626 +-0.2315415233853133 20 -0.030675252470457135 +-0.05195301084127257 30 0.0 70 @@ -3445,9 +3475,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.035658783132023364 +-0.2336339111865664 20 -0.03073859404865303 +-0.04857799963283188 30 0.0 70 @@ -3467,9 +3497,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.035843204187391564 +-0.23583689605604086 20 -0.030778802306374368 +-0.04510141098539078 30 0.0 70 @@ -3489,9 +3519,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.03606868051476314 +-0.23824729900171532 20 -0.030826258436102018 +-0.04164084191496459 30 0.0 70 @@ -3511,9 +3541,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.036334228869552565 +-0.24096194103156843 20 -0.03088077916708409 +-0.038313889437569026 30 0.0 70 @@ -3533,9 +3563,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.03663886600717414 +-0.24407764315357894 20 -0.03094218122856872 +-0.03523815056921925 30 0.0 70 @@ -3555,9 +3585,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.03698160868304223 +-0.24769122637572547 20 -0.03101028134980403 +-0.032531222325930864 30 0.0 70 @@ -3577,9 +3607,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.037361473652571364 +-0.25189527154487523 20 -0.031084896260038145 +-0.03031250748991371 30 0.0 70 @@ -3599,9 +3629,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.037777477671175785 +-0.2556710587823801 20 -0.031165842688519192 +-0.028897726461885542 30 0.0 70 @@ -3621,9 +3651,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.03822863749426986 +-0.259176350329518 20 -0.03125293736449529 +-0.027895338568229233 30 0.0 70 @@ -3643,9 +3673,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.038713969877267995 +-0.26261507934256245 20 -0.03134599701721458 +-0.027062875912520212 30 0.0 70 @@ -3665,9 +3695,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.03923249157558467 +-0.2661911789777869 20 -0.03144483837592516 +-0.026157870598334243 30 0.0 70 @@ -3687,9 +3717,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.039783219344634135 +-0.2701085823914646 20 -0.031549278169875176 +-0.02493785472924681 30 0.0 70 @@ -3709,9 +3739,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.0403651699398308 +-0.2742767322269781 20 -0.03165913312831275 +-0.023287611479191006 30 0.0 70 @@ -3731,9 +3761,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.04097736011658909 +-0.2773537617614356 20 -0.031774219980486004 +-0.02171288824969564 30 0.0 70 @@ -3753,9 +3783,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.04161880663032336 +-0.28041119952074683 20 -0.031894355455643064 +-0.019761313469147057 30 0.0 70 @@ -3775,9 +3805,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.04228852623644791 +-0.283386801947182 20 -0.032019356283032055 +-0.01745533373208813 30 0.0 70 @@ -3797,9 +3827,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.042985535690377275 +-0.28621832548301135 20 -0.03214903919190111 +-0.014817395633061614 30 0.0 70 @@ -3819,9 +3849,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.043708851747525646 +-0.28884352657050505 20 -0.032283220911498337 +-0.011869945766610546 30 0.0 70 @@ -3841,9 +3871,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.044457491163307605 +-0.29120016165193335 20 -0.03242171817107187 +-0.008635430727277682 30 0.0 70 @@ -3863,9 +3893,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.0452304706931374 +-0.29322598716956644 20 -0.03256434769986984 +-0.005136297109605947 30 0.0 70 @@ -3885,9 +3915,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.046026807092429456 +-0.2948587595656746 20 -0.03271092622714036 +-0.0013949915081380992 30 0.0 70 @@ -3907,9 +3937,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.04684551711659807 +-0.296036235282528 20 -0.03286127048213157 +0.0025660394825827715 30 0.0 70 @@ -3929,9 +3959,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.04768561752105771 +-0.2966961707623968 20 -0.033015197194091586 +0.006724349268014074 30 0.0 70 @@ -3951,9 +3981,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.0485461250612228 +-0.29677131237060905 20 -0.03317252309226853 +0.010823895979494569 30 0.0 70 @@ -3973,9 +4003,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.049426056492507586 +-0.29606911246228473 20 -0.03333306490591054 +0.014181500952964832 30 0.0 70 @@ -3995,9 +4025,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.05032442857032654 +-0.2945903225789705 20 -0.033496639364265716 +0.01733619705562711 30 0.0 70 @@ -4017,9 +4047,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.051240258050093995 +-0.29243224340788126 20 -0.03366306319658221 +0.020277968983556938 30 0.0 70 @@ -4039,9 +4069,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.05217256168722437 +-0.28969217563623184 20 -0.033832153132108134 +0.022996801432829728 30 0.0 70 @@ -4061,9 +4091,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.053120356237132016 +-0.28646741995123726 20 -0.03400372590009162 +0.025482679099520955 30 0.0 70 @@ -4083,9 +4113,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.05408265845523133 +-0.2828552770401123 20 -0.03417759822978078 +0.02772558667970615 30 0.0 70 @@ -4105,9 +4135,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.055058485096936643 +-0.278953047590072 20 -0.03435358685042376 +0.029715508869460727 30 0.0 70 @@ -4127,9 +4157,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.0560468529176624 +-0.27485803228833117 20 -0.03453150849126866 +0.03144243036486016 30 0.0 70 @@ -4149,9 +4179,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.057046778672822956 +-0.27066753182210485 20 -0.03471117988156362 +0.032896335861979986 30 0.0 70 @@ -4171,9 +4201,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.05805727911783265 +-0.2664788468786078 20 -0.03489241775055677 +0.034067210056895614 30 0.0 70 @@ -4193,9 +4223,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.05907737100810595 +-0.26238927814505497 20 -0.03507503882749623 +0.034945037645682575 30 0.0 70 @@ -4215,9 +4245,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.060106071099057135 +-0.2584961263086613 20 -0.035258859841630116 +0.03551980332441629 30 0.0 70 @@ -4237,9 +4267,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.06114239614610065 +-0.2548966920566418 20 -0.035443697522206566 +0.035781491789172226 30 0.0 70 @@ -4259,9 +4289,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.062185362904650876 +-0.2516882760762112 20 -0.035629368598473696 +0.03572008773602592 30 0.0 70 @@ -4281,9 +4311,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.06323398813012213 +-0.24842500237321694 20 -0.03581568979967963 +0.03541667366752854 30 0.0 70 @@ -4303,9 +4333,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.06428728857792881 +-0.24477200340806995 20 -0.036002477855072504 +0.035075636354958084 30 0.0 70 @@ -4325,9 +4355,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.06534428100348538 +-0.24082262719541409 20 -0.036189549493900436 +0.03471373073340123 30 0.0 70 @@ -4347,9 +4377,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.06640398216220611 +-0.23664618716419447 20 -0.036376721445411556 +0.034341520250685376 30 0.0 70 @@ -4369,9 +4399,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.06746540880950547 +-0.23231199674335595 20 -0.036563810438853976 +0.03396956835463805 30 0.0 70 @@ -4391,9 +4421,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.06852757770079776 +-0.22788936936184365 20 -0.03675063320347584 +0.033608438493086756 30 0.0 70 @@ -4413,9 +4443,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.0695895055914974 +-0.22344761844860245 20 -0.036937006468525255 +0.03326869411385891 30 0.0 70 @@ -4435,9 +4465,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.07065020923701876 +-0.21905605743257744 20 -0.03712274696325035 +0.03296089866478208 30 0.0 70 @@ -4457,9 +4487,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.07170870539277618 +-0.21478399974271362 20 -0.037307671416899266 +0.03269561559368367 30 0.0 70 @@ -4479,9 +4509,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.07276401081418413 +-0.2107007588079559 20 -0.037491596558720114 +0.03248340834839114 30 0.0 70 @@ -4501,9 +4531,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.07381514225665695 +-0.20687564805724937 20 -0.037674339117961024 +0.03233484037673212 30 0.0 70 @@ -4523,9 +4553,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.07486111647560895 +-0.2033779809195389 20 -0.03785571582387011 +0.032260475126533905 30 0.0 70 @@ -4545,9 +4575,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.0759009502264546 +-0.20027707082376967 20 -0.03803554340569551 +0.03227087604562412 30 0.0 70 @@ -4567,9 +4597,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.07693366026460824 +-0.19698654858711234 20 -0.03821363859268535 +0.03238592413156666 30 0.0 70 @@ -4589,9 +4619,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.07795826334548428 +-0.19318285904709753 20 -0.038389818114087745 +0.0325402622832367 30 0.0 70 @@ -4611,9 +4641,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.07897377622449706 +-0.18931970887041638 20 -0.03856389869915083 +0.03270409348936071 30 0.0 70 @@ -4633,9 +4663,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.07997921565706095 +-0.18539709781429586 20 -0.038735697077122724 +0.032877414975028385 30 0.0 70 @@ -4655,9 +4685,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.08097359839859036 +-0.18141502563596268 20 -0.03890502997725155 +0.033060223965329594 30 0.0 70 @@ -4677,9 +4707,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.08195594120449967 +-0.17737349209264375 20 -0.039071714128785434 +0.03325251768535398 30 0.0 70 @@ -4699,9 +4729,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.08292526083020324 +-0.1726381225467503 20 -0.039235566260972514 +0.03338911516149823 30 0.0 70 @@ -4721,9 +4751,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.08388057403111548 +-0.1659840849214246 20 -0.0393964031030609 +0.033316250989470886 30 0.0 70 @@ -4743,9 +4773,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.08482089756265071 +-0.15964888467818186 20 -0.039554041384298716 +0.03324643511774267 30 0.0 70 @@ -4765,9 +4795,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.08574524818022339 +-0.1536325212730734 20 -0.03970829783393409 +0.033179667593903006 30 0.0 70 @@ -4787,9 +4817,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.0866526426392478 +-0.14793499416215058 20 -0.039858989181215165 +0.03311594846554122 30 0.0 70 @@ -4809,9 +4839,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.08754209769513843 +-0.14255630280146458 20 -0.04000593215539004 +0.03305527778024664 30 0.0 70 @@ -4831,9 +4861,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.0884126301033096 +-0.13749644664706695 20 -0.040148943485706856 +0.032997655585608576 30 0.0 70 @@ -4853,9 +4883,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.08926325661917567 +-0.13275542515500882 20 -0.040287839901413734 +0.03294308192921647 30 0.0 70 @@ -4875,9 +4905,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.09009299399815102 +-0.12833323778134154 20 -0.040422438131758796 +0.032891556858659576 30 0.0 70 @@ -4897,9 +4927,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.09090085899565006 +-0.12422988398211648 20 -0.04055255490599017 +0.032843080421527227 30 0.0 70 @@ -4919,9 +4949,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.0916858683670872 +-0.12044536321338492 20 -0.04067800695335598 +0.03279765266540885 30 0.0 70 @@ -4941,9 +4971,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.09244703886787675 +-0.11697967493119824 20 -0.040798611003104356 +0.03275527363789377 30 0.0 70 @@ -4963,9 +4993,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.09318338725343314 +-0.1138328185916076 20 -0.040914183784483416 +0.032715943386571245 30 0.0 70 @@ -4985,9 +5015,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.0938939302791707 +-0.11100479365066446 20 -0.04102454202674129 +0.03267966195903066 30 0.0 70 @@ -5007,9 +5037,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.09457768470050384 +-0.10849559956442015 20 -0.0411295024591261 +0.03264642940286139 30 0.0 70 @@ -5029,9 +5059,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.09523366727284693 +-0.1063052357889259 20 -0.041228881810885964 +0.03261624576565281 30 0.0 70 @@ -5051,9 +5081,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.09586089475161436 +-0.10443370178023303 20 -0.041322496811269024 +0.03258911109499418 30 0.0 70 @@ -5073,9 +5103,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.0964583838922205 +-0.10288099699439296 20 -0.0414101641895234 +0.03256502543847484 30 0.0 70 @@ -5095,9 +5125,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.09702515145007973 +-0.10164712088745684 20 -0.04149170067489721 +0.03254398884368426 30 0.0 70 @@ -5117,9 +5147,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.09756021418060645 +-0.09994337892822275 20 -0.04156692299663858 +0.03249160766593884 30 0.0 70 @@ -5139,9 +5169,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.09806258883921501 +-0.09722703434661115 20 -0.04163564788399564 +0.03234615182366568 30 0.0 70 @@ -5161,9 +5191,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.09853129218131981 +-0.093771592897088 20 -0.04169769206621651 +0.032118852135510856 30 0.0 70 @@ -5183,9 +5213,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.0989653409623352 +-0.08970115316225025 20 -0.04175287227254933 +0.03182178565127325 30 0.0 70 @@ -5205,9 +5235,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.09936375193767558 +-0.0851398137246947 20 -0.0418010052322422 +0.03146702942075169 30 0.0 70 @@ -5227,9 +5257,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.09972554186275531 +-0.08021167316701835 20 -0.04184190767454325 +0.031066660493744958 30 0.0 70 @@ -5249,9 +5279,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.09995304002710165 +-0.07504083007181808 20 -0.0418661899502048 +0.03063275592005199 30 0.0 70 @@ -5271,9 +5301,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.10016700328101996 +-0.06975138302169073 20 -0.04188885151517696 +0.03017739274947162 30 0.0 70 @@ -5293,9 +5323,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.10039375511735624 +-0.06446743059923335 20 -0.0419127552788092 +0.029712648031802624 30 0.0 70 @@ -5315,9 +5345,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.10063329554049044 +-0.0593130713870427 20 -0.0419379012411015 +0.029250598816843887 30 0.0 70 @@ -5337,9 +5367,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.10088562455480257 +-0.05441240396771574 20 -0.04196428940205388 +0.028803322154394295 30 0.0 70 @@ -5359,9 +5389,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.10115074216467274 +-0.04988952692384935 20 -0.04199191976166633 +0.028382895094252625 30 0.0 70 @@ -5381,9 +5411,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.1014286483744809 +-0.045868538838040485 20 -0.04202079231993884 +0.028001394686217707 30 0.0 70 @@ -5403,9 +5433,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.10171934318860718 +-0.04247353829288597 20 -0.042050907076871424 +0.027670897980088482 30 0.0 70 @@ -5425,9 +5455,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.10202282661143153 +-0.03982862387098274 20 -0.04208226403246408 +0.027403482025663728 30 0.0 70 @@ -5447,9 +5477,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.10233909864733406 +-0.03805789415492772 20 -0.04211486318671681 +0.02721122387274233 30 0.0 70 @@ -5469,9 +5499,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.10266815930069476 +-0.036264793274075446 20 -0.042148704539629614 +0.0269216897066466 30 0.0 70 @@ -5491,9 +5521,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.10301000857589368 +-0.032322371975447184 20 -0.042183788091202476 +0.025941611713292345 30 0.0 70 @@ -5513,9 +5543,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.10336464647731086 +-0.027830560327799014 20 -0.04222011384143542 +0.0246016350498387 30 0.0 70 @@ -5535,9 +5565,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.10373207300932633 +-0.023812359863461197 20 -0.042257681790328426 +0.02324602396599168 30 0.0 70 @@ -5557,9 +5587,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.10411228817632015 +-0.021294698693667236 20 -0.04229649193788151 +0.022218126153409845 30 0.0 70 @@ -5579,9 +5609,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.1045052919826723 +-0.01872618634040374 20 -0.042336544284094654 +0.02064227908050753 30 0.0 70 @@ -5601,9 +5631,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.10491108443276287 +-0.015093256261121235 20 -0.04237783882896788 +0.018146755728090103 30 0.0 70 @@ -5623,9 +5653,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.10532966553097192 +-0.011096281688223297 20 -0.04242037557250117 +0.01523911360159308 30 0.0 70 @@ -5645,9 +5675,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.1057610352816794 +-0.007435635854113387 20 -0.042464154514694534 +0.012426910206452146 30 0.0 70 @@ -5667,9 +5697,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.1062051936892654 +-0.004811691991195022 20 -0.04250917565554796 +0.010217703048102877 30 0.0 70 @@ -5689,9 +5719,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.10666214075810995 +-0.003023760328472236 20 -0.042555438995061465 +0.008011242975078714 30 0.0 70 @@ -5711,9 +5741,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.10713187649259309 +-0.0014008764041681387 20 -0.04260294453323504 +0.004592179313590128 30 0.0 70 @@ -5733,15135 +5763,127 @@ AcDbVertex 100 AcDb2dVertex 10 --0.10761440089709487 +0.0 20 -0.04265169227006868 +0.0 30 0.0 70 0 0 -VERTEX +SEQEND 5 -D9 +2E 330 17 100 AcDbEntity 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.10810971397599525 - 20 -0.0427016822055624 - 30 -0.0 - 70 0 0 -VERTEX +ENDSEC + 0 +SECTION + 2 +OBJECTS + 0 +DICTIONARY 5 -DA +A 330 -17 -100 -AcDbEntity - 8 0 100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1086178157336744 - 20 -0.04275291433971619 - 30 -0.0 - 70 -0 +AcDbDictionary +281 +1 + 3 +ACAD_COLOR +350 +B + 3 +ACAD_GROUP +350 +C + 3 +ACAD_LAYOUT +350 +D + 3 +ACAD_MATERIAL +350 +E + 3 +ACAD_MLEADERSTYLE +350 +F + 3 +ACAD_MLINESTYLE +350 +10 + 3 +ACAD_PLOTSETTINGS +350 +11 + 3 +ACAD_PLOTSTYLENAME +350 +12 + 3 +ACAD_SCALELIST +350 +14 + 3 +ACAD_TABLESTYLE +350 +15 + 3 +ACAD_VISUALSTYLE +350 +16 0 -VERTEX +DICTIONARY 5 -DB +B 330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex +A 100 -AcDb2dVertex - 10 --0.10913870617451224 - 20 -0.042805388672530045 - 30 -0.0 - 70 -0 +AcDbDictionary +281 +1 0 -VERTEX +DICTIONARY 5 -DC +C 330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex +A 100 -AcDb2dVertex - 10 --0.10967238530288886 - 20 -0.04285910520400396 - 30 -0.0 - 70 -0 +AcDbDictionary +281 +1 0 -VERTEX +DICTIONARY 5 -DD +D 330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex +A 100 -AcDb2dVertex - 10 --0.1102188531231843 - 20 -0.042914063934137964 - 30 -0.0 - 70 -0 +AcDbDictionary +281 +1 + 3 +Model +350 +1A + 3 +Layout1 +350 +1E 0 -VERTEX +DICTIONARY 5 -DE +E 330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.11077810963977855 - 20 -0.042970264862932026 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -DF -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.11135015485705171 - 20 -0.04302770799038617 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -E0 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.11193498877938374 - 20 -0.043086393316500375 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -E1 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.11253261141115478 - 20 -0.04314632084127465 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -E2 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.11314302275674479 - 20 -0.043207490564709 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -E3 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.11376622282053381 - 20 -0.04326990248680342 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -E4 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1144022116069019 - 20 -0.043333556607557916 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -E5 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1150509891202291 - 20 -0.043398452926972475 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -E6 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.11571255536489541 - 20 -0.04346459144504711 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -E7 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1163869103452809 - 20 -0.043531972161781805 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -E8 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.11707405406576563 - 20 -0.04360059507717658 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -E9 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1177739865307296 - 20 -0.04367046019123142 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -EA -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1184867077445528 - 20 -0.04374156750394633 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -EB -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.11921221771161533 - 20 -0.04381391701532132 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -EC -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.11995051643629726 - 20 -0.04388750872535637 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -ED -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.12070160392297855 - 20 -0.04396234263405149 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -EE -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.12146548017603925 - 20 -0.044038418741406696 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -EF -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.12224214519985946 - 20 -0.04411573704742196 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -F0 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.12303159899881913 - 20 -0.04419429755209729 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -F1 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.12383384157729835 - 20 -0.0442741002554327 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -F2 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.12464887293967716 - 20 -0.04435514515742818 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -F3 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1254766930903356 - 20 -0.04443743225808373 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -F4 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.12631730203365363 - 20 -0.04452096155739934 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -F5 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.12717069977401138 - 20 -0.04460573305537503 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -F6 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.12803688631578886 - 20 -0.04469174675201079 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -F7 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.12891586166336602 - 20 -0.04477900264730662 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -F8 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.12980762582112307 - 20 -0.04486750074126252 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -F9 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.13071217879343994 - 20 -0.04495724103387849 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -FA -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1316295205846966 - 20 -0.04504822352515453 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -FB -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1325596511992732 - 20 -0.04514044821509064 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -FC -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1335025706415498 - 20 -0.04523391510368683 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -FD -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.13445827891590628 - 20 -0.04532862419094308 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -FE -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.13542677602672284 - 20 -0.0454245754768594 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -FF -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.13640806197837946 - 20 -0.045521768961435796 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -100 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.13740213677525612 - 20 -0.04562020464467226 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -101 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.13840900042173293 - 20 -0.04571988252656879 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -102 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.13942865292218992 - 20 -0.0458208026071254 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -103 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.14046109428100703 - 20 -0.04592296488634208 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -104 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.14150632450256445 - 20 -0.04602636936421882 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -105 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.14256434359124212 - 20 -0.04613101604075564 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -106 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.14363515155142006 - 20 -0.046236904915952524 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -107 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1447187483874784 - 20 -0.04634403598980948 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -108 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.14581513410379707 - 20 -0.046452409262326515 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -109 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.14692430870475612 - 20 -0.04656202473350361 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -10A -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1480462721947357 - 20 -0.046672882403340776 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -10B -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.14918102457811575 - 20 -0.04678498227183802 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -10C -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.15032856585927631 - 20 -0.04689832433899533 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -10D -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.15148889604259747 - 20 -0.047012908604812706 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -10E -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1526620151324592 - 20 -0.04712873506929016 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -10F -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.15384792313324158 - 20 -0.04724580373242769 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -110 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.15504662004932454 - 20 -0.04736411459422527 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -111 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.15625810588508832 - 20 -0.04748366765468294 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -112 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1574823806449128 - 20 -0.04760446291380067 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -113 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.15871944433317808 - 20 -0.04772650037157848 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -114 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.15996929695426418 - 20 -0.04784978002801635 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -115 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.16123193851255116 - 20 -0.0479743018831143 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -116 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.16250736901241894 - 20 -0.048100065936872306 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -117 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.16379558845824768 - 20 -0.04822707218929039 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -118 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.16509659685441744 - 20 -0.04835532064036855 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -119 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.16641039420530815 - 20 -0.04848481129010678 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -11A -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.16773698051529995 - 20 -0.04861554413850508 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -11B -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.16907635578877278 - 20 -0.04874751918556344 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -11C -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.17042852003010675 - 20 -0.048880736431281886 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -11D -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1717934732436818 - 20 -0.04901519587566039 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -11E -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1731712154338781 - 20 -0.04915089751869897 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -11F -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1741596943528995 - 20 -0.049211918564809734 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -120 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.174990619985511 - 20 -0.0492435630486022 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -121 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.17581912524139995 - 20 -0.04927537929387907 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -122 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.17664521012056636 - 20 -0.04930736732298474 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -123 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.17746887462301025 - 20 -0.04933952715826361 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -124 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.17829011874873155 - 20 -0.049371858822060084 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -125 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.17910894249773035 - 20 -0.04940436233671857 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -126 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1799253458700066 - 20 -0.049437037724583466 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -127 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.18073932886556032 - 20 -0.049469885007999176 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -128 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.18155089148439152 - 20 -0.0495029042093101 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -129 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.18236003372650017 - 20 -0.04953609535086065 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -12A -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.18316675559188622 - 20 -0.049569458454995205 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -12B -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.18397105708054978 - 20 -0.0496029935440582 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -12C -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.18477293819249083 - 20 -0.049636700640394014 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -12D -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.18557239892770933 - 20 -0.04967057976634706 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -12E -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.18636943928620528 - 20 -0.04970463094426174 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -12F -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1871640592679787 - 20 -0.04973885419648245 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -130 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.18795625887302958 - 20 -0.0497732495453536 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -131 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.18874603810135787 - 20 -0.04980781701321958 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -132 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.18953339695296367 - 20 -0.049842556622424816 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -133 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.19031833542784693 - 20 -0.04987746839531369 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -134 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.19110085352600767 - 20 -0.049912552354230616 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -135 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.19188095124744584 - 20 -0.04994780852152 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -136 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.19265862859216148 - 20 -0.049983236919526226 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -137 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.19343388556015456 - 20 -0.05001883757059371 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -138 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.19420672215142512 - 20 -0.050054610497066845 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -139 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.19497713836597316 - 20 -0.05009055572129005 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -13A -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.19574513420379863 - 20 -0.05012667326560772 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -13B -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.19651070966490158 - 20 -0.05016296315236426 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -13C -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.19727386474928202 - 20 -0.05019942540390406 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -13D -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1980345994569399 - 20 -0.050236060042571536 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -13E -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.19877267146877997 - 20 -0.05027190026431345 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -13F -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.19932081065879115 - 20 -0.05030141913164245 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -140 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1998880238290493 - 20 -0.05033657175251838 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -141 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.20047376447210924 - 20 -0.05037722525371138 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -142 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.201077486080526 - 20 -0.05042324676199161 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -143 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.20169864214685454 - 20 -0.050474503404129185 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -144 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.20233668616364978 - 20 -0.05053086230689427 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -145 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.20299107162346663 - 20 -0.050592190597056996 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -146 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.20366125201886015 - 20 -0.05065835540138751 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -147 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.20434668084238522 - 20 -0.05072922384665595 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -148 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.20504681158659682 - 20 -0.050804663059632466 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -149 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.20576109774404983 - 20 -0.0508845401670872 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -14A -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.20648899280729932 - 20 -0.050968722295790296 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -14B -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.20722995026890012 - 20 -0.051057076572511885 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -14C -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.20798342362140726 - 20 -0.05114947012402212 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -14D -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2087488663573757 - 20 -0.05124577007709115 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -14E -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.20952573196936033 - 20 -0.051345843558489104 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -14F -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.21031347394991617 - 20 -0.05144955769498613 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -150 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.21111154579159813 - 20 -0.05155677961335237 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -151 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.21191940098696116 - 20 -0.05166737644035797 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -152 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.21273649302856018 - 20 -0.05178121530277307 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -153 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2135622754089502 - 20 -0.05189816332736782 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -154 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.21439620162068618 - 20 -0.052018087640912355 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -155 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.21523772515632303 - 20 -0.05214085537017682 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -156 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.21608629950841574 - 20 -0.05226633364193135 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -157 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2169413781695192 - 20 -0.05239438958294611 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -158 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2178024146321884 - 20 -0.05252489031999122 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -159 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.21866886238897826 - 20 -0.05265770297983682 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -15A -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.21954017493244382 - 20 -0.05279269468925308 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -15B -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.22041580575513997 - 20 -0.05292973257501012 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -15C -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.22129520834962163 - 20 -0.05306868376387809 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -15D -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2221778362084438 - 20 -0.053209415382627134 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -15E -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.22306314282416143 - 20 -0.053351794558027396 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -15F -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2239505816893294 - 20 -0.053495688416849006 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -160 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2248396062965028 - 20 -0.05364096408586212 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -161 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.22572967013823647 - 20 -0.05378748869183689 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -162 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.22662022670708537 - 20 -0.05393512936154343 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -163 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2275107294956045 - 20 -0.054083753221751914 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -164 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2284006319963488 - 20 -0.05423322739923246 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -165 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.22928938770187318 - 20 -0.054383419020755226 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -166 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2301764501047326 - 20 -0.054534195213090345 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -167 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.23106127269748206 - 20 -0.05468542310300797 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -168 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.23194330897267648 - 20 -0.05483696981727823 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -169 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.23282201242287082 - 20 -0.054988702482671285 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -16A -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.23369683654062 - 20 -0.05514048822595727 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -16B -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.23456723481847902 - 20 -0.05529219417390632 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -16C -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.23543266074900282 - 20 -0.05544368745328859 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -16D -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.23629256782474628 - 20 -0.055594835190874214 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -16E -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.23714640953826444 - 20 -0.05574550451343334 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -16F -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.23799363938211224 - 20 -0.05589556254773611 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -170 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2388337108488446 - 20 -0.05604487642055266 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -171 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2396660774310165 - 20 -0.05619331325865315 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -172 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.24049019262118287 - 20 -0.056340740188807706 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -173 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.24130550991189864 - 20 -0.05648702433778647 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -174 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2421114827957188 - 20 -0.0566320328323596 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -175 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2429075647651983 - 20 -0.05677563279929723 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -176 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2436932093128921 - 20 -0.0569176913653695 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -177 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.24446786993135514 - 20 -0.05705807565734656 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -178 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.24523100011314236 - 20 -0.057196652801998545 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -179 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2459820533508087 - 20 -0.0573332899260956 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -17A -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.24672048313690909 - 20 -0.057467854156407865 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -17B -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.24744574296399857 - 20 -0.05760021261970549 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -17C -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.24815728632463205 - 20 -0.05773023244275863 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -17D -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.24885456671136444 - 20 -0.0578577807523374 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -17E -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.24953703761675075 - 20 -0.057982724675211955 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -17F -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2502041525333459 - 20 -0.05810493133815244 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -180 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2508553649537048 - 20 -0.058224267867928996 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -181 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.25148412627767264 - 20 -0.058339357657130234 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -182 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.25207940009355373 - 20 -0.05843858306040156 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -183 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2526951223608269 - 20 -0.058526155972813854 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -184 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.253330508534902 - 20 -0.058602088401815584 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -185 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2539847740711891 - 20 -0.058666392354855226 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -186 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.254657134425098 - 20 -0.058719079839381266 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -187 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.25534680505203877 - 20 -0.05876016286284218 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -188 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2560530014074214 - 20 -0.05878965343268644 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -189 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2567749389466557 - 20 -0.05880756355636253 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -18A -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.25751183312515175 - 20 -0.05881390524131893 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -18B -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2582628993983195 - 20 -0.058808690495004104 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -18C -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.25902735322156883 - 20 -0.05879193132486654 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -18D -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.25980441005030974 - 20 -0.05876363973835472 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -18E -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2605932853399522 - 20 -0.058723827742917116 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -18F -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.26139319454590615 - 20 -0.058672507346002205 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -190 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2622033531235815 - 20 -0.058609690555058463 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -191 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.26302297652838835 - 20 -0.05853538937753437 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -192 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2638512802157365 - 20 -0.0584496158208784 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -193 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.26468747964103595 - 20 -0.058352381892539044 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -194 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.26553079025969667 - 20 -0.058243699599964775 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -195 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.26638042752712865 - 20 -0.05812358095060406 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -196 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2672356068987418 - 20 -0.057992037951905384 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -197 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2680955438299461 - 20 -0.05784908261131722 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -198 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2689594537761515 - 20 -0.057694726936288056 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -199 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2698265521927679 - 20 -0.05752898293426636 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -19A -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.27069605453520534 - 20 -0.05735186261270061 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -19B -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2715671762588737 - 20 -0.057163377979039295 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -19C -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.272439132819183 - 20 -0.05696354104073088 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -19D -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2733111396715432 - 20 -0.05675236380522385 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -19E -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.27418241227136425 - 20 -0.05652985827996668 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -19F -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.275052166074056 - 20 -0.05629603647240784 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1A0 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2759196165350286 - 20 -0.05605091038999582 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1A1 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2767839791096918 - 20 -0.055794492040179104 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1A2 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2776444692534557 - 20 -0.05552679343040616 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1A3 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2785003024217302 - 20 -0.055247826568125456 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1A4 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2793506940699253 - 20 -0.05495760346078548 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1A5 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.28019485965345087 - 20 -0.054656136115834704 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1A6 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.28103201462771693 - 20 -0.054343436540721615 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1A7 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2818613744481334 - 20 -0.05401951674289469 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1A8 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2826821545701103 - 20 -0.053684388729802406 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1A9 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.28349357044905754 - 20 -0.05333806450889323 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1AA -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.28429483754038504 - 20 -0.05298055608761565 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1AB -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.28508517129950284 - 20 -0.052611875473418133 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1AC -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.28586378718182087 - 20 -0.05223203467374917 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1AD -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.286629900642749 - 20 -0.051841045696057235 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1AE -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2873827271376973 - 20 -0.0514389205477908 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1AF -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.28812148212207567 - 20 -0.05102567123639837 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1B0 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.28884538105129404 - 20 -0.050601309769328376 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1B1 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.28955363938076245 - 20 -0.05016584815402933 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1B2 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2902454725658908 - 20 -0.04971929839794969 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1B3 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2909200960620891 - 20 -0.04926167250853794 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1B4 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.29157672532476714 - 20 -0.04879298249324257 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1B5 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2922145758093351 - 20 -0.048313240359512045 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1B6 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.29283286297120276 - 20 -0.047822458114794866 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1B7 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.29343080226578017 - 20 -0.04732064776653947 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1B8 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2940076091484773 - 20 -0.04680782132219436 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1B9 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.294562499074704 - 20 -0.04628399078920801 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1BA -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.29509468749987033 - 20 -0.045749168175028894 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1BB -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2956033898793862 - 20 -0.04520336548710549 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1BC -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2960878216686616 - 20 -0.04464659473288631 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1BD -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.29654719832310644 - 20 -0.04407886791981978 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1BE -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2969807352981307 - 20 -0.04350019705535439 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1BF -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.29738764804914436 - 20 -0.042910594146938624 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1C0 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.29776715203155735 - 20 -0.042310071202020974 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1C1 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2981184627007796 - 20 -0.041698640228049894 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1C2 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2984407955122211 - 20 -0.04107631323247386 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1C3 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2987333659212918 - 20 -0.04044310222274142 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1C4 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.29899538938340164 - 20 -0.03979901920630096 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1C5 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.29922608135396056 - 20 -0.03914407619060099 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1C6 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2994246572883786 - 20 -0.03847828518308999 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1C7 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.29959033264206564 - 20 -0.037801658191216427 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1C8 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.29972232287043166 - 20 -0.03711420722242881 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1C9 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2998198434288866 - 20 -0.03641594428417562 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1CA -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.29989971397700854 - 20 -0.035572126171891955 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1CB -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.29995773683528665 - 20 -0.03469691872510711 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1CC -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2999909354403105 - 20 -0.03382626096445765 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1CD -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2999997914953523 - 20 -0.032960375780174786 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1CE -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.29998478670368384 - 20 -0.03209948606248974 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1CF -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.29994640276857737 - 20 -0.031243814701633746 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1D0 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2998851213933048 - 20 -0.03039358458783807 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1D1 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.29980142428113826 - 20 -0.029549018611333862 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1D2 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.29969579313534966 - 20 -0.028710339662352376 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1D3 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.29956870965921106 - 20 -0.02787777063112485 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1D4 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2994206555559945 - 20 -0.027051534407882508 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1D5 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.29925211252897205 - 20 -0.026231853882856572 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1D6 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2990635622814157 - 20 -0.025418951946278273 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1D7 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.29885548651659744 - 20 -0.02461305148837887 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1D8 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2986283669377893 - 20 -0.023814375399389513 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1D9 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2983826852482634 - 20 -0.023023146569541478 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1DA -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2981189231512917 - 20 -0.022239587889065972 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1DB -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2978375623501462 - 20 -0.02146392224819424 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1DC -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.29753908454809896 - 20 -0.020696372537157497 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1DD -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.29722397144842205 - 20 -0.019937161646186974 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1DE -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.29689270475438745 - 20 -0.019186512465513887 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1DF -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.29654576616926714 - 20 -0.018444647885369467 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1E0 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.29618363739633324 - 20 -0.017711790795984957 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1E1 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2958068001388578 - 20 -0.016988164087591635 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1E2 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2954157361001127 - 20 -0.016273990650420588 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1E3 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.29501092698337006 - 20 -0.01556949337470312 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1E4 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2945928544919019 - 20 -0.014874895150670454 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1E5 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2941620003289803 - 20 -0.014190418868553815 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1E6 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2937188461978772 - 20 -0.013516287418584433 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1E7 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.29326387380186464 - 20 -0.01285272369099353 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1E8 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2927975648442147 - 20 -0.012199950576012322 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1E9 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2923204010281994 - 20 -0.011558190963872061 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1EA -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2918328640570907 - 20 -0.010927667744803943 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1EB -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2913354356341607 - 20 -0.010308603809039224 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1EC -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.29082859746268136 - 20 -0.009701222046809121 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1ED -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2903128312459248 - 20 -0.009105745348344838 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1EE -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.289788618687163 - 20 -0.008522396603877679 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1EF -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.28925644148966795 - 20 -0.007951398703638764 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1F0 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2887167813567117 - 20 -0.007392974537859351 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1F1 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2881701199915663 - 20 -0.006847346996770691 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1F2 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.28761693909750374 - 20 -0.006314738970604 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1F3 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2870577203777961 - 20 -0.005795373349590495 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1F4 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.28649294553571536 - 20 -0.005289473023961419 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1F5 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2859230962745336 - 20 -0.004797260883947975 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1F6 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.28534865429752276 - 20 -0.0043189598197814205 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1F7 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.284770101307955 - 20 -0.003854792721692965 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1F8 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2841879190091022 - 20 -0.0034049824799138317 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1F9 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.28360258910423647 - 20 -0.0029697519846752438 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1FA -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2830145932966298 - 20 -0.002549324126208438 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1FB -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.28242441328955437 - 20 -0.00214392179474468 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1FC -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.28183253078628195 - 20 -0.0017537678805151016 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1FD -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.28123942749008474 - 20 -0.0013790852737509823 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1FE -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.28064558510423465 - 20 -0.001020096864683559 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -1FF -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.28005148533200386 - 20 -0.0006770255435440409 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -200 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2794576098766643 - 20 -0.00035009420056365814 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -201 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2785802222856699 - 20 --0.00011148951773121141 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -202 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2777085656861816 - 20 --0.0005522300202456462 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -203 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2768600535620914 - 20 --0.0009640968413820866 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -204 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.27603322577419515 - 20 --0.0013491701503893783 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -205 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.27522662218328836 - 20 --0.0017095301165163876 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -206 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.27443878265016663 - 20 --0.0020472569090119394 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -207 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.27366824703562553 - 20 --0.0023644306971249 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -208 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.27291355520046084 - 20 --0.0026631316501040878 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -209 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2721732470054679 - 20 --0.0029454399371984033 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -20A -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2714458623114425 - 20 --0.0032134357276566855 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -20B -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2707299409791802 - 20 --0.003469199190727766 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -20C -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2700240228694765 - 20 --0.0037148104956605046 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -20D -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.26932664784312715 - 20 --0.003952349811703747 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -20E -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2686363557609277 - 20 --0.004183897308106345 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -20F -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2679516864836738 - 20 --0.004411533154117138 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -210 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.267271179872161 - 20 --0.004637337518984992 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -211 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.26659337578718484 - 20 --0.004863390571958746 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -212 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.26591681408954104 - 20 --0.005091772482287253 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -213 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.26524003464002516 - 20 --0.005324563419219379 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -214 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.26456157729943286 - 20 --0.005563843552003948 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -215 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.26387998192855966 - 20 --0.005811693049889828 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -216 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.26319378838820123 - 20 --0.006070192082125829 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -217 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2625015365391531 - 20 --0.006341420817960873 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -218 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2618017662422109 - 20 --0.006627459426643757 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -219 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.26109301735817025 - 20 --0.006930388077423348 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -21A -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2603738297478268 - 20 --0.007252286939548498 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -21B -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2596427432719761 - 20 --0.007595236182268046 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -21C -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2588982977914137 - 20 --0.007961315974830858 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -21D -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.25813903316693537 - 20 --0.00835260648648576 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -21E -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2573045899793085 - 20 --0.008806547854173986 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -21F -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2564582507712165 - 20 --0.009302596558563934 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -220 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2556403556510659 - 20 --0.009818998684686171 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -221 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.25485004869347944 - 20 --0.010354882190157184 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -222 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.25408647397308 - 20 --0.010909375032593448 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -223 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2533487755644903 - 20 --0.011481605169611435 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -224 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.25263609754233307 - 20 --0.01207070055882773 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -225 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2519475839812312 - 20 --0.012675789157858781 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -226 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2512823789558074 - 20 --0.013295998924321074 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -227 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.25063962654068456 - 20 --0.013930457815831111 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -228 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2500184708104854 - 20 --0.014578293790005366 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -229 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.24941805583983273 - 20 --0.015238634804460367 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -22A -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2488375257033493 - 20 --0.01591060881681259 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -22B -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.24827602447565794 - 20 --0.01659334378467852 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -22C -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.24773269623138144 - 20 --0.01728596766567466 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -22D -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2472066850451426 - 20 --0.017987608417417512 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -22E -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.24669713499156418 - 20 --0.018697393997523577 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -22F -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.24620319014526898 - 20 --0.019414452363609315 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -230 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.24572399458087985 - 20 --0.02013791147329117 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -231 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.24525869237301948 - 20 --0.020866899284185773 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -232 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2448064275963107 - 20 --0.021600543753909567 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -233 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.24436634432537627 - 20 --0.022337972840079025 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -234 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.24393758663483905 - 20 --0.02307831450031065 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -235 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.24351929859932178 - 20 --0.02382069669222093 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -236 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.24311062429344726 - 20 --0.024564247373426364 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -237 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2427107077918383 - 20 --0.025308094501543442 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -238 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.24231869316911764 - 20 --0.026051366034188665 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -239 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.24193372449990813 - 20 --0.02679318992897852 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -23A -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2415549458588325 - 20 --0.02753269414352951 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -23B -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2411815013205136 - 20 --0.028269006635458123 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -23C -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2408125349595742 - 20 --0.029001255362380844 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -23D -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2404471908506371 - 20 --0.029728568281914135 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -23E -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.24008461306832501 - 20 --0.03045007335167458 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -23F -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.23972394568726083 - 20 --0.031164898529278638 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -240 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.23936433278206726 - 20 --0.03187217177234277 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -241 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.23900491842736715 - 20 --0.03257102103848351 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -242 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2386448466977833 - 20 --0.03326057428531733 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -243 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.23828326166793845 - 20 --0.033939959470460725 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -244 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2379193074124554 - 20 --0.034608304551530196 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -245 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.23755212800595693 - 20 --0.03526473748614222 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -246 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.23718086752306589 - 20 --0.0359083862319133 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -247 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.23680467003840502 - 20 --0.03653837874645995 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -248 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.23642267962659713 - 20 --0.03715384298739865 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -249 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.236034040362265 - 20 --0.037753906912345904 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -24A -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2356378963200314 - 20 --0.03833769847891819 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -24B -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.23523339157451917 - 20 --0.03890434564473195 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -24C -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.23481967020035105 - 20 --0.03945297636740377 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -24D -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.23439587627214986 - 20 --0.039982718604550126 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -24E -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.23396115386453836 - 20 --0.04049270031378751 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -24F -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.23351464705213937 - 20 --0.04098204945273238 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -250 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2330554999095757 - 20 --0.041449893979001264 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -251 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.23258285651147007 - 20 --0.04189536185021065 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -252 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.23209586093244533 - 20 --0.042317581023977015 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -253 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.23159365724712422 - 20 --0.04271567945791687 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -254 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.23107538953012957 - 20 --0.04308878510964669 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -255 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.23054020185608418 - 20 --0.04343602593678301 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -256 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.22998723829961082 - 20 --0.043756529896942266 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -257 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.22941564293533223 - 20 --0.04404942494774102 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -258 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.22882455983787137 - 20 --0.04431383904679568 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -259 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.22821313308185082 - 20 --0.04454890015172281 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -25A -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.22758050674189345 - 20 --0.0447537362201389 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -25B -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.22634914199949607 - 20 --0.04509063202675693 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -25C -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.22487076492189773 - 20 --0.04548455689656774 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -25D -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2235075582558282 - 20 --0.04583839999248406 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -25E -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.22225232938837636 - 20 --0.046153793603088 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -25F -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.22109788570663097 - 20 --0.046432370016961634 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -260 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.22003703459768087 - 20 --0.04667576152268709 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -261 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.21906258344861482 - 20 --0.04688560040884641 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -262 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.21816733964652166 - 20 --0.047063518964021715 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -263 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.21734411057849018 - 20 --0.04721114947679509 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -264 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.21658570363160917 - 20 --0.04733012423574864 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -265 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2158849261929675 - 20 --0.04742207552946442 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -266 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2152345856496539 - 20 --0.04748863564652455 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -267 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.21462748938875717 - 20 --0.04753143687551114 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -268 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.21405644479736613 - 20 --0.04755211150500626 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -269 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.21351425926256956 - 20 --0.04755229182359199 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -26A -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.21299374017145634 - 20 --0.04753361011985046 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -26B -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2124876949111152 - 20 --0.04749769868236372 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -26C -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.21198893086863496 - 20 --0.047446189799713885 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -26D -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.21149025543110445 - 20 --0.04738071576048302 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -26E -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.21098447598561243 - 20 --0.04730290885325327 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -26F -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.21046439991924773 - 20 --0.047214401366606686 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -270 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.20992283461909914 - 20 --0.047116825589125354 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -271 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2093525874722555 - 20 --0.047011813809391394 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -272 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.20874646586580556 - 20 --0.046900998315986896 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -273 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2081133859820591 - 20 --0.04678880157640792 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -274 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.20797595180754558 - 20 --0.04676409716209739 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -275 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.20782347651287447 - 20 --0.04673509760131351 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -276 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.20765596008657225 - 20 --0.04670180290552981 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -277 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.20747340251716528 - 20 --0.04666421308621991 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -278 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.20727580379318003 - 20 --0.04662232815485736 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -279 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2070631639031429 - 20 --0.04657614812291578 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -27A -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.20683548283558031 - 20 --0.04652567300186871 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -27B -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.20659276057901868 - 20 --0.04647090280318975 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -27C -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.20633499712198441 - 20 --0.04641183753835248 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -27D -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.20606219245300395 - 20 --0.04634847721883046 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -27E -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.20577434656060375 - 20 --0.04628082185609731 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -27F -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.20547145943331013 - 20 --0.04620887146162656 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -280 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.20515353105964967 - 20 --0.04613262604689184 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -281 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.20482056142814864 - 20 --0.04605208562336669 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -282 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.20447255052733349 - 20 --0.04596725020252471 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -283 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.20410949834573067 - 20 --0.04587811979583949 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -284 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2037314048718666 - 20 --0.045784694414784576 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -285 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2033382700942677 - 20 --0.04568697407083356 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -286 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2029300940014604 - 20 --0.04558495877546002 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -287 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.20250687658197108 - 20 --0.04547864854013756 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -288 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.20206861782432617 - 20 --0.045368043376339755 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -289 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.20161531771705216 - 20 --0.04525314329554015 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -28A -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.20114697624867536 - 20 --0.04513394830921235 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -28B -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.20066359340772227 - 20 --0.045010458428829926 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -28C -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.20016516918271926 - 20 --0.04488267366586648 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -28D -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.19965170356219286 - 20 --0.04475059403179558 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -28E -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.19912319653466934 - 20 --0.0446142195380908 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -28F -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.19857964808867518 - 20 --0.04447355019622571 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -290 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.19802105821273683 - 20 --0.0443285860176739 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -291 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1974474268953807 - 20 --0.044179327013908955 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -292 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.19685875412513315 - 20 --0.044025773196404445 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -293 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.19625503989052068 - 20 --0.04386792457663396 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -294 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.19563628418006965 - 20 --0.04370578116607108 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -295 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.19500248698230654 - 20 --0.04353934297618937 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -296 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1943536482857577 - 20 --0.04336861001846242 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -297 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.19368976807894964 - 20 --0.04319358230436381 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -298 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1930108463504087 - 20 --0.04301425984536713 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -299 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.19231688308866132 - 20 --0.04283064265294593 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -29A -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.191607878282234 - 20 --0.04264273073857384 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -29B -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.19088383191965302 - 20 --0.042450524113724374 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -29C -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1901447439894449 - 20 --0.04225402278987118 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -29D -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.189390614480136 - 20 --0.042053226778487766 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -29E -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1886214433802528 - 20 --0.04184813609104777 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -29F -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.18783723067832167 - 20 --0.04163875073902474 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2A0 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.18703797636286904 - 20 --0.04142507073389227 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2A1 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.18622368042242138 - 20 --0.04120709608712394 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2A2 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.18539434284550504 - 20 --0.040984826810193324 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2A3 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.18454996362064646 - 20 --0.040758262914573996 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2A4 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1836905427363721 - 20 --0.040527404411739555 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2A5 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.18281608018120835 - 20 --0.04029225131316356 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2A6 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1819265759436816 - 20 --0.04005280363031961 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2A7 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1810220300123183 - 20 --0.03980906137468127 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2A8 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.18010244237564504 - 20 --0.03956102455772214 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2A9 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.17916781302218793 - 20 --0.03930869319091578 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2AA -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.17821814194047353 - 20 --0.03905206728573574 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2AB -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.17725342911902828 - 20 --0.03879114685365568 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2AC -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1762736745463786 - 20 --0.038525931906149107 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2AD -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.17527887821105081 - 20 --0.038256422454689624 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2AE -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.17426904010157152 - 20 --0.03798261851075082 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2AF -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.173244160206467 - 20 --0.03770452008580626 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2B0 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1722042385142637 - 20 --0.037422127191329556 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2B1 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.17114927501348806 - 20 --0.037135439838794256 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2B2 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.17007926969266654 - 20 --0.03684445803967392 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2B3 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.16899422254032548 - 20 --0.03654918180544219 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2B4 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.16789413354499133 - 20 --0.03624961114757259 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2B5 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1667790026951906 - 20 --0.03594574607753876 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2B6 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.16564882997944957 - 20 --0.035637586606814216 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2B7 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1645036153862947 - 20 --0.03532513274687255 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2B8 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.16334335890425244 - 20 --0.03500838450918737 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2B9 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.16216806052184918 - 20 --0.034687341905232226 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2BA -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.16097772022761137 - 20 --0.034362004946480736 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2BB -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1597723380100654 - 20 --0.03403237364440643 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2BC -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1585519138577377 - 20 --0.03369844801048292 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2BD -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.15731644775915474 - 20 --0.03336022805618378 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2BE -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.15606593970284288 - 20 --0.03301771379298258 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2BF -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.15480038967732856 - 20 --0.03267090523235292 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2C0 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.15351979767113816 - 20 --0.03231980238576836 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2C1 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1522241636727982 - 20 --0.031964405264702504 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2C2 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.15091348767083515 - 20 --0.031604713880628944 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2C3 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.14958776965377518 - 20 --0.031240728245021186 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2C4 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.14824700961014486 - 20 --0.030872448369352858 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2C5 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1468912075284706 - 20 --0.030499874265097544 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2C6 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.14552036339727878 - 20 --0.03012300594372882 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2C7 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1441344772050959 - 20 --0.029741843416720253 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2C8 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.14273354894044837 - 20 --0.029356386695545432 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2C9 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.14194240269108277 - 20 --0.02908606215953797 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2CA -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.14119068819494135 - 20 --0.028823231876943294 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2CB -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.14043897369879987 - 20 --0.028560401594348604 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2CC -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.13968725920265843 - 20 --0.02829757131175393 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2CD -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.138935544706517 - 20 --0.02803474102915924 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2CE -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.13818383021037556 - 20 --0.027771910746564564 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2CF -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.13743211571423417 - 20 --0.027509080463969916 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2D0 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.13668040121809272 - 20 --0.027246250181375227 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2D1 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.13592868672195127 - 20 --0.02698341989878055 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2D2 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1351769722258098 - 20 --0.026720589616185862 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2D3 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.13442525772966837 - 20 --0.026457759333591187 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2D4 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.13367354323352693 - 20 --0.026194929050996497 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2D5 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.13292182873738545 - 20 --0.025932098768401822 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2D6 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.13217011424124403 - 20 --0.025669268485807133 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2D7 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.13141839974510258 - 20 --0.025406438203212457 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2D8 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.13066668524896113 - 20 --0.025143607920617768 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2D9 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1299149707528197 - 20 --0.024880777638023092 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2DA -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.12916325625667824 - 20 --0.024617947355428403 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2DB -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1284115417605368 - 20 --0.024355117072833728 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2DC -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.12765982726439531 - 20 --0.02409228679023904 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2DD -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.12690811276825395 - 20 --0.023829456507644377 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2DE -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.12615639827211253 - 20 --0.0235666262250497 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2DF -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.12540468377597105 - 20 --0.023303795942455012 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2E0 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1246529692798296 - 20 --0.023040965659860337 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2E1 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.12390125478368816 - 20 --0.022778135377265647 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2E2 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.12314954028754671 - 20 --0.022515305094670972 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2E3 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.12239782579140526 - 20 --0.022252474812076282 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2E4 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.12164611129526381 - 20 --0.021989644529481607 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2E5 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1208943967991224 - 20 --0.02172681424688693 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2E6 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.12014268230298092 - 20 --0.021463983964292242 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2E7 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.11939096780683947 - 20 --0.021201153681697553 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2E8 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.11863925331069805 - 20 --0.020938323399102877 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2E9 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.11788753881455658 - 20 --0.020675493116508202 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2EA -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.11713582431841518 - 20 --0.020412662833913554 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2EB -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.11638410982227376 - 20 --0.02014983255131885 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2EC -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.11563239532613229 - 20 --0.01988700226872419 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2ED -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.11488068082999084 - 20 --0.019624171986129486 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2EE -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.11412896633384942 - 20 --0.019361341703534797 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2EF -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.11337725183770794 - 20 --0.01909851142094012 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2F0 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1126255373415665 - 20 --0.01883568113834546 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2F1 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.11187382284542507 - 20 --0.018572850855750757 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2F2 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.11112210834928363 - 20 --0.018310020573156095 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2F3 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.11037039385314215 - 20 --0.018047190290561392 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2F4 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1096186793570007 - 20 --0.01778436000796673 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2F5 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.10886696486085926 - 20 --0.017521529725372027 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2F6 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.10811525036471781 - 20 --0.017258699442777366 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2F7 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.10736353586857644 - 20 --0.016995869160182704 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2F8 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.106611821372435 - 20 --0.016733038877588015 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2F9 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.10586010687629352 - 20 --0.016470208594993326 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2FA -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1051083923801521 - 20 --0.01620737831239865 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2FB -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.10435667788401065 - 20 --0.015944548029803975 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2FC -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.10360496338786918 - 20 --0.015681717747209285 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2FD -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.10285324889172773 - 20 --0.01541888746461461 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2FE -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.10210153439558631 - 20 --0.01515605718201992 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -2FF -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.10134981989944486 - 20 --0.014893226899425245 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -300 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.10059810540330338 - 20 --0.014630396616830556 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -301 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.09984639090716196 - 20 --0.01436756633423588 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -302 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.09909467641102052 - 20 --0.014104736051641191 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -303 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.09834296191487904 - 20 --0.013841905769046516 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -304 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.09759124741873768 - 20 --0.013579075486451854 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -305 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.09683953292259623 - 20 --0.013316245203857165 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -306 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.09608781842645481 - 20 --0.01305341492126249 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -307 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.09533610393031333 - 20 --0.012790584638667814 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -308 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.09458438943417188 - 20 --0.012527754356073124 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -309 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.09383267493803041 - 20 --0.012264924073478449 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -30A -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.09308096044188899 - 20 --0.01200209379088376 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -30B -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.09232924594574754 - 20 --0.011739263508289084 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -30C -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.09157753144960609 - 20 --0.011476433225694395 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -30D -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.09082581695346464 - 20 --0.01121360294309972 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -30E -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.0900741024573232 - 20 --0.01095077266050503 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -30F -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.08932238796118175 - 20 --0.010687942377910355 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -310 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.08857067346504027 - 20 --0.010425112095315665 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -311 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.08781339995867424 - 20 --0.010179066664029617 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -312 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.08705340728699001 - 20 --0.00994123146868598 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -313 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.08629341461530574 - 20 --0.009703396273342331 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -314 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.0855334219436214 - 20 --0.009465561077998681 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -315 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.0847734292719371 - 20 --0.009227725882655018 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -316 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.08401343660025282 - 20 --0.008989890687311368 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -317 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.08325344392856851 - 20 --0.008752055491967718 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -318 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.08249345125688423 - 20 --0.008514220296624068 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -319 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.0817334585851999 - 20 --0.008276385101280419 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -31A -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.08097346591351562 - 20 --0.008038549905936769 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -31B -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.08021347324183131 - 20 --0.0078007147105931054 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -31C -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.079453480570147 - 20 --0.007562879515249456 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -31D -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.07869348789846273 - 20 --0.007325044319905792 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -31E -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.07793349522677842 - 20 --0.007087209124562142 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -31F -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.0771735025550942 - 20 --0.0068493739292185205 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -320 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.07641350988340989 - 20 --0.006611538733874871 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -321 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.07565351721172561 - 20 --0.006373703538531221 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -322 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.07489352454004128 - 20 --0.006135868343187557 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -323 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.07413353186835697 - 20 --0.005898033147843908 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -324 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.0733735391966727 - 20 --0.005660197952500258 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -325 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.07261354652498839 - 20 --0.005422362757156594 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -326 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.07185355385330411 - 20 --0.005184527561812945 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -327 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.0710935611816198 - 20 --0.004946692366469295 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -328 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.07033356850993547 - 20 --0.004708857171125645 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -329 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.06957357583825119 - 20 --0.004471021975781982 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -32A -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.06881358316656688 - 20 --0.004233186780438332 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -32B -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.0680535904948826 - 20 --0.003995351585094682 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -32C -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.06729359782319835 - 20 --0.0037575163897510602 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -32D -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.06653360515151407 - 20 --0.0035196811944074036 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -32E -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.06577361247982977 - 20 --0.003281845999063747 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -32F -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.06501361980814549 - 20 --0.0030440108037200903 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -330 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.06425362713646118 - 20 --0.0028061756083764405 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -331 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.06349363446477685 - 20 --0.002568340413032791 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -332 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.06273364179309257 - 20 --0.002330505217689134 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -333 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.061973649121408264 - 20 --0.0020926700223454844 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -334 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.06121365644972396 - 20 --0.0018548348270018278 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -335 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.060504285896758825 - 20 --0.0016749033629925442 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -336 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.05982983194144559 - 20 --0.0015185883853187018 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -337 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.05916547701376473 - 20 --0.0013568687471811197 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -338 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.05850873972343856 - 20 --0.0011897785906160516 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -339 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.05785713868018941 - 20 --0.0010173520576597928 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -33A -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.05720819249373971 - 20 --0.0008396232903486317 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -33B -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.05655941977381171 - 20 --0.0006566264307188152 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -33C -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.05590833913012777 - 20 --0.0004683956208066317 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -33D -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.05525246917241025 - 20 --0.0002749650026483627 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -33E -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.05458932851038148 - 20 --7.636871828028274e-05 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -33F -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.05391643575376387 - 20 -0.00012735909026131975 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -340 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.05323130951227964 - 20 -0.0003361842809401841 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -341 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.05253146839565126 - 20 -0.0005500727117200149 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -342 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.051814431013601 - 20 -0.0007689902405645516 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -343 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.05107771597585123 - 20 -0.0009929027254375056 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -344 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.05031884189212435 - 20 -0.0012217760243026024 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -345 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.04953532737214256 - 20 -0.0014555759951235675 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -346 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.04872469102562832 - 20 -0.0016942684958641194 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -347 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.04788445146230408 - 20 -0.0019378193844879557 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -348 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.047012127291891914 - 20 -0.0021861945189588436 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -349 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.04610523712411435 - 20 -0.0024393597572404946 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -34A -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.04516129956869369 - 20 -0.002697280957296627 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -34B -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.04417783323535224 - 20 -0.002959923977090953 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -34C -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.0431523567338124 - 20 -0.0032272546745872044 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -34D -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.04208238867379649 - 20 -0.0034992389077491 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -34E -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.0414744415029098 - 20 -0.0034866198640948268 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -34F -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.040885322957459835 - 20 -0.003433465737780486 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -350 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.040252175894184894 - 20 -0.0033764242771729958 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -351 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.039577151513876696 - 20 -0.0033156915003708515 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -352 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.03886240101732691 - 20 -0.003251463425472542 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -353 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.038110075605327254 - 20 -0.0031839360705766054 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -354 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.037322326478669454 - 20 -0.003113305453781523 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -355 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.03650130483814512 - 20 -0.0030397675931858117 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -356 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.03564916188454598 - 20 -0.0029635185068879605 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -357 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.03476804881866369 - 20 -0.002884754212986493 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -358 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.03386011684129003 - 20 -0.0028036707295799185 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -359 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.03292751715321657 - 20 -0.0027204640747667264 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -35A -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.03197240095523507 - 20 -0.002635330266645433 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -35B -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.03099691944813726 - 20 -0.0025484653233145416 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -35C -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.030003223832714754 - 20 -0.0024600652628725683 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -35D -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.02899346530975938 - 20 -0.0023703261034180023 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -35E -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.027969795080062643 - 20 -0.00227944386304936 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -35F -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.02693436434441626 - 20 -0.0021876145598651586 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -360 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.02588932430361207 - 20 -0.0020950342119638726 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -361 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.024836826158441794 - 20 -0.0020018988374440466 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -362 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.023779021109696874 - 20 -0.0019084044544041556 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -363 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.022718060358169145 - 20 -0.0018147470809427232 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -364 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.021656095104650275 - 20 -0.0017211227351582384 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -365 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.020595276549931985 - 20 -0.0016277274351492246 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -366 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.019537755894805886 - 20 -0.0015347571990141848 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -367 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.018485684340063813 - 20 -0.0014424080448516147 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -368 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.017441213086497376 - 20 -0.001350875990760038 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -369 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.016406493334898242 - 20 -0.0012603570548379434 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -36A -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.015383676286058134 - 20 -0.0011710472551838408 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -36B -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.01437491314076872 - 20 -0.0010831426098962468 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -36C -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.013382355099821719 - 20 -0.0009968391370736573 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -36D -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.012408153364008856 - 20 -0.000912332854814582 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -36E -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.011454459134121742 - 20 -0.0008298197812175306 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -36F -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.01052342361095221 - 20 -0.0007494959343810059 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -370 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.00961719799529176 - 20 -0.0006715573324035107 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -371 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.00873793348793217 - 20 -0.0005961999933835546 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -372 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.007887781289665108 - 20 -0.0005236199354196405 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -373 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.007068892601282295 - 20 -0.0004540131766102712 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -374 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.006283418623575454 - 20 -0.00038757573505396326 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -375 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.0055335105573361965 - 20 -0.0003245036288492195 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -376 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.0048213196033563 - 20 -0.0002649928760945497 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -377 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.0041489969624274314 - 20 -0.00020923949488844268 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -378 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.0035186938353412023 - 20 -0.00015743950332942902 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -379 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.0029325614228893904 - 20 -0.00010978891951599068 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -37A -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.0023927509258636626 - 20 -6.648376154665125e-05 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -37B -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.0019014135450557412 - 20 -2.7720047519913527e-05 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -37C -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.0014607004812572932 - 20 --6.3062044657197225e-06 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -37D -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.0010727629352599855 - 20 --3.539897631173877e-05 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -37E -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.0007397521078555402 - 20 --5.936224991964778e-05 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -37F -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.00046381919983556896 - 20 --7.800000719092315e-05 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -380 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.000247115411991905 - 20 --9.111623002706903e-05 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -381 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --9.179194511615973e-05 - 20 --9.85149003295896e-05 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -382 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 -0.0 - 20 --9.999999999996123e-05 - 30 -0.0 - 70 -0 - 0 -SEQEND - 5 -30 -330 -2F -100 -AcDbEntity - 8 -0 - 0 -ENDSEC - 0 -SECTION - 2 -OBJECTS - 0 -DICTIONARY - 5 -A -330 -0 -100 -AcDbDictionary -281 -1 - 3 -ACAD_COLOR -350 -B - 3 -ACAD_GROUP -350 -C - 3 -ACAD_LAYOUT -350 -D - 3 -ACAD_MATERIAL -350 -E - 3 -ACAD_MLEADERSTYLE -350 -F - 3 -ACAD_MLINESTYLE -350 -10 - 3 -ACAD_PLOTSETTINGS -350 -11 - 3 -ACAD_PLOTSTYLENAME -350 -12 - 3 -ACAD_SCALELIST -350 -14 - 3 -ACAD_TABLESTYLE -350 -15 - 3 -ACAD_VISUALSTYLE -350 -16 - 3 -EZDXF_META -350 -2D - 0 -DICTIONARY - 5 -B -330 -A -100 -AcDbDictionary -281 -1 - 0 -DICTIONARY - 5 -C -330 -A -100 -AcDbDictionary -281 -1 - 0 -DICTIONARY - 5 -D -330 -A -100 -AcDbDictionary -281 -1 - 3 -Model -350 -1A - 3 -Layout1 -350 -1E - 0 -DICTIONARY - 5 -E -330 -A +A 100 AcDbDictionary 281 @@ -20980,22 +6002,24 @@ D AcDbPlotSettings 1 + 2 +Adobe PDF 4 -A3 +A4 6 40 -7.5 +3.175 41 -20.0 +3.175 42 -7.5 +3.175 43 -20.0 +3.175 44 -420.0 +209.91 45 -297.0 +297.03 46 0.0 47 @@ -21015,9 +6039,9 @@ A3 70 1024 72 -1 - 73 0 + 73 +1 74 5 7 @@ -21045,13 +6069,13 @@ Model 71 0 10 -0.0 +-3.175 20 -0.0 +-3.175 11 -420.0 +293.857 21 -297.0 +206.735 12 0.0 22 @@ -21059,17 +6083,17 @@ Model 32 0.0 14 -1e+20 +29.068 24 -1e+20 +20.356 34 -1e+20 +0.0 15 --1e+20 +261.614 25 --1e+20 +183.204 35 --1e+20 +0.0 146 0.0 13 @@ -21104,22 +6128,24 @@ D AcDbPlotSettings 1 + 2 +Adobe PDF 4 -A3 +A4 6 40 -7.5 +3.175 41 -20.0 +3.175 42 -7.5 +3.175 43 -20.0 +3.175 44 -420.0 +209.91 45 -297.0 +297.03 46 0.0 47 @@ -21139,9 +6165,9 @@ A3 70 0 72 -1 - 73 0 + 73 +1 74 5 7 @@ -21169,13 +6195,13 @@ Layout1 71 1 10 -0.0 +-3.175 20 -0.0 +-3.175 11 -420.0 +293.857 21 -297.0 +206.735 12 0.0 22 @@ -21183,17 +6209,17 @@ Layout1 32 0.0 14 -1e+20 +29.068 24 -1e+20 +20.356 34 -1e+20 +0.0 15 --1e+20 +261.614 25 --1e+20 +183.204 35 --1e+20 +0.0 146 0.0 13 @@ -21703,7 +6729,7 @@ Standard 47 1.0 49 -1.0 +0.0 140 1.0 294 @@ -21724,53 +6750,9 @@ Standard 0 272 9 -273 +272 9 0 -DICTIONARY - 5 -2D -330 -A -100 -AcDbDictionary -280 -1 -281 -1 - 3 -CREATED_BY_EZDXF -350 -2E - 3 -WRITTEN_BY_EZDXF -350 -385 - 0 -DICTIONARYVAR - 5 -2E -330 -2D -100 -DictionaryVariables -280 -0 - 1 -1.4.2 @ 2025-06-30T10:46:10.542611+00:00 - 0 -DICTIONARYVAR - 5 -385 -330 -2D -100 -DictionaryVariables -280 -0 - 1 -1.4.2 @ 2025-06-30T10:46:10.554003+00:00 - 0 ENDSEC 0 EOF From 162bc6f92902725fbb141237c2b690942cc4e557 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Tue, 17 Feb 2026 20:08:13 +0100 Subject: [PATCH 107/134] Implement Kelvin-Vogt damping for TLSPH --- .../structure/total_lagrangian_sph/system.jl | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/schemes/structure/total_lagrangian_sph/system.jl b/src/schemes/structure/total_lagrangian_sph/system.jl index a1cd48fb54..9b4f2b2f1f 100644 --- a/src/schemes/structure/total_lagrangian_sph/system.jl +++ b/src/schemes/structure/total_lagrangian_sph/system.jl @@ -454,6 +454,8 @@ function update_quantities!(system::TotalLagrangianSPHSystem, v, u, v_ode, u_ode # Precompute PK1 stress tensor @trixi_timeit timer() "stress tensor" compute_pk1_corrected!(system, semi) + # @trixi_timeit timer() "KV damping" apply_kelvin_voigt_damping!(system.pk1_rho2, v, system, semi) + return system end @@ -556,6 +558,54 @@ end return deformation_grad end +@inline function apply_kelvin_voigt_damping!(pk1_rho2, v, system, semi) + (; mass, material_density) = system + + # Compute bulk modulus from Young's modulus and Poisson's ratio. + # See the table at the end of https://en.wikipedia.org/wiki/Lam%C3%A9_parameters + # TODO Should we compute the sound speed per particle and then use the maximum? + E = maximum(system.young_modulus) + K = E / (ndims(system) * (1 - 2 * maximum(system.poisson_ratio))) + + # Newton–Laplace equation + sound_speed = sqrt(K / minimum(system.material_density)) + + # Loop over all pairs of particles and neighbors within the kernel cutoff + initial_coords = initial_coordinates(system) + foreach_point_neighbor(system, system, initial_coords, initial_coords, + semi) do particle, neighbor, initial_pos_diff, initial_distance + # Only consider particles with a distance > 0. + # See `src/general/smoothing_kernels.jl` for more details. + initial_distance^2 < eps(initial_smoothing_length(system)^2) && return + + volume = @inbounds mass[neighbor] / material_density[neighbor] + v_diff = @inbounds current_velocity(v, system, particle) - + current_velocity(v, system, neighbor) + + grad_kernel = smoothing_kernel_grad(system, initial_pos_diff, + initial_distance, particle) + + # Multiply by L_{0a} + L = @inbounds correction_matrix(system, particle) + + dF = -volume * v_diff * grad_kernel' * L' + + F = deformation_gradient(system, particle) + + alpha = 1.0 + rho = material_density[particle] + + h = smoothing_length(system, particle) + P_rho2 = alpha / rho * sound_speed * h / 2 * F * (dF' * F + F' * dF) + + for j in 1:ndims(system), i in 1:ndims(system) + @inbounds pk1_rho2[i, j, particle] += P_rho2[i, j] + end + end + + return pk1_rho2 +end + # First Piola-Kirchhoff stress tensor @propagate_inbounds function pk1_stress_tensor(system, particle) (; lame_lambda, lame_mu) = system From c529b744b47f08cce8760094c1f12782d406b132 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Tue, 17 Feb 2026 20:09:28 +0100 Subject: [PATCH 108/134] Add pvd argument to interpolation --- src/general/interpolation.jl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/general/interpolation.jl b/src/general/interpolation.jl index 116388753b..cb24e3079b 100644 --- a/src/general/interpolation.jl +++ b/src/general/interpolation.jl @@ -155,7 +155,7 @@ function interpolate_plane_2d_vtk(min_corner, max_corner, resolution, semi, ref_ v_ode, u_ode; include_wall_velocity=false, smoothing_length=initial_smoothing_length(ref_system), cut_off_bnd=true, clip_negative_pressure=false, - output_directory="out", filename="plane") + output_directory="out", filename="plane", pvd=nothing, t=-1) # Don't filter out particles without neighbors to keep 2D grid structure filter_no_neighbors = false @trixi_timeit timer() "interpolate plane" begin @@ -177,6 +177,10 @@ function interpolate_plane_2d_vtk(min_corner, max_corner, resolution, semi, ref_ vtk["density"] = density vtk["velocity"] = velocity vtk["pressure"] = pressure + + if !isnothing(pvd) && t >= 0 + pvd[t] = vtk + end end end From 6aa3184aaeb4a96228a6c5d29be8bfdf67d18ba5 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Tue, 17 Feb 2026 20:10:51 +0100 Subject: [PATCH 109/134] Update fin with Antuono, velocity averaging, open boundaries and interpolation --- examples/fsi/fin_2d.jl | 43 +++++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/examples/fsi/fin_2d.jl b/examples/fsi/fin_2d.jl index f5f74fd7cc..4bb6516559 100644 --- a/examples/fsi/fin_2d.jl +++ b/examples/fsi/fin_2d.jl @@ -246,14 +246,15 @@ structure_system = TotalLagrangianSPHSystem(structure, smoothing_kernel, smoothi modulus, poisson_ratio; n_clamped_particles, clamped_particles_motion=boundary_motion, boundary_model=boundary_model_structure, + velocity_averaging=nothing, viscosity=ArtificialViscosityMonaghan(alpha=0.1), penalty_force=PenaltyForceGanzenmueller(alpha=0.1)) # ========================================================================================== # ==== Fluid fluid_density_calculator = ContinuityDensity() -density_diffusion = DensityDiffusionMolteniColagrossi(delta=0.1) -# density_diffusion = DensityDiffusionAntuono(fluid, delta=0.1) +# density_diffusion = DensityDiffusionMolteniColagrossi(delta=0.1) +density_diffusion = DensityDiffusionAntuono(fluid, delta=0.1) fluid_system = WeaklyCompressibleSPHSystem(fluid, fluid_density_calculator, state_equation, smoothing_kernel, @@ -280,13 +281,9 @@ if periodic else periodic_box = nothing - function velocity_function2d(pos, t) - return SVector(1.0, 0.0) - end - open_boundary_model = BoundaryModelDynamicalPressureZhang() # open_boundary_model = BoundaryModelMirroringTafuni(; mirror_method=ZerothOrderMirroring()) - reference_velocity_in = velocity_function2d + reference_velocity_in = SVector(1.0, 0.0) reference_pressure_in = nothing reference_density_in = nothing boundary_type_in = InFlow() @@ -299,8 +296,8 @@ else reference_velocity=reference_velocity_in, initial_condition=inlet.fluid, boundary_type=boundary_type_in) - reference_velocity_out = SVector(1.0, 0.0) - reference_pressure_out = nothing + reference_velocity_out = nothing + reference_pressure_out = 0.0 reference_density_out = nothing boundary_type_out = OutFlow() face_out = ([min_coords_outlet[1], 0.0], [min_coords_outlet[1], tank_size[2]]) @@ -317,6 +314,8 @@ else shifting_technique=nothing) wall = union(tank.boundary, inlet.boundary, outlet.boundary) + min_corner = minimum(wall.coordinates, dims=2) .- 5 * fluid_particle_spacing + max_corner = maximum(wall.coordinates, dims=2) .+ 5 * fluid_particle_spacing end # ========================================================================================== @@ -331,8 +330,6 @@ boundary_system = WallBoundarySystem(wall, boundary_model) # ========================================================================================== # ==== Simulation -min_corner = minimum(wall.coordinates, dims=2) .- fluid_particle_spacing / 2 -max_corner = maximum(wall.coordinates, dims=2) .+ fluid_particle_spacing / 2 cell_list = FullGridCellList(; min_corner, max_corner) neighborhood_search = GridNeighborhoodSearch{2}(; periodic_box, cell_list, update_strategy=ParallelUpdate()) @@ -342,7 +339,8 @@ semi = Semidiscretization(fluid_system, boundary_system, open_boundary_system, s ode = semidiscretize(semi, tspan) info_callback = InfoCallback(interval=100) -saving_callback = SolutionSavingCallback(dt=0.01, prefix="") +prefix = "" +saving_callback = SolutionSavingCallback(dt=0.01; prefix) split_cfl = 1.0 # SSPRK104 CFL = 2.5, 15k RHS evaluations @@ -368,10 +366,25 @@ function total_volume(system, data, t) return nothing end pp_cb = PostprocessCallback(; total_volume, interval=100, - filename="total_volume", write_file_interval=50) + filename="$(prefix)_total_volume", write_file_interval=50) + +function plane_vtk(system, dv_ode, du_ode, v_ode, u_ode, semi, t) + return nothing +end +function plane_vtk(system::WeaklyCompressibleSPHSystem, dv_ode, du_ode, v_ode, u_ode, semi, t) + resolution = fluid_particle_spacing / 6 + pvd = TrixiParticles.paraview_collection("out/$(prefix)_plane"; append=t > 0) + interpolate_plane_2d_vtk(min_corner, max_corner, resolution, + semi, semi.systems[1], v_ode, u_ode, include_wall_velocity=true, + filename="$(prefix)_plane_$(round(Int, t * 1000))", pvd=pvd, t=t) + TrixiParticles.vtk_save(pvd) + return nothing +end +interpolate_cb = PostprocessCallback(; plane_vtk, dt=0.01, filename="plane") -callbacks = CallbackSet(info_callback, saving_callback, UpdateCallback(), - stepsize_callback, split_integration, pp_cb) +callbacks = CallbackSet(info_callback, saving_callback, + stepsize_callback, split_integration, pp_cb, interpolate_cb, + UpdateCallback()) dt_fluid = 1.25e-4 sol = solve(ode, From 3059ae620c454c8f07ff46c8e528762cca206d22 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Tue, 17 Feb 2026 20:11:03 +0100 Subject: [PATCH 110/134] Add simplified fin example file --- examples/fsi/fin_2d_simplified.jl | 253 ++++++++++++++++++++++++++++++ 1 file changed, 253 insertions(+) create mode 100644 examples/fsi/fin_2d_simplified.jl diff --git a/examples/fsi/fin_2d_simplified.jl b/examples/fsi/fin_2d_simplified.jl new file mode 100644 index 0000000000..18134e9b1d --- /dev/null +++ b/examples/fsi/fin_2d_simplified.jl @@ -0,0 +1,253 @@ +using TrixiParticles +using OrdinaryDiffEqLowStorageRK +using OrdinaryDiffEqSymplecticRK + +# ========================================================================================== +# ==== Resolution +n_particles_y = 4 + +# ========================================================================================== +# ==== Experiment Setup +tspan = (0.0, 2.0) + +fin_length = 0.6 +fin_thickness = 2e-3 +real_thickness = 1e-3 +real_modulus = 125e9 +poisson_ratio = 0.3 +flexural_rigidity = real_modulus * real_thickness^3 / (1 - poisson_ratio^2) / 12 +modulus = 12 * (1 - poisson_ratio^2) * flexural_rigidity / fin_thickness^3 + +fiber_volume_fraction = 0.6 +fiber_density = 1800.0 +epoxy_density = 1250.0 +density = fiber_volume_fraction * fiber_density + + (1 - fiber_volume_fraction) * epoxy_density + +clamp_radius = 0.05 + +tank_size = (0.8, 0.2) +center = (0.05, 0.1) +initial_fluid_size = tank_size +initial_velocity = (1.0, 0.0) + +# The structure starts at the position of the first particle and ends +# at the position of the last particle. +particle_spacing = fin_thickness / (n_particles_y - 1) +fluid_particle_spacing = particle_spacing + +smoothing_length_structure = sqrt(2) * particle_spacing +smoothing_length_fluid = 1.5 * fluid_particle_spacing +smoothing_kernel = WendlandC2Kernel{2}() + +# Beam and clamped particles +length_clamp = round(Int, 0.01 / particle_spacing) * particle_spacing # m +n_particles_per_dimension = (round(Int, (length_clamp) / particle_spacing) + 2,# + n_particles_clamp_x, + n_particles_y) +shape_sampled = RectangularShape(particle_spacing, n_particles_per_dimension, + (-length_clamp, 0.0), density=density, place_on_shell=true) +length_clamp = 0.0 +n_particles_per_dimension = (round(Int, (fin_length + length_clamp) / particle_spacing) + 2,# + n_particles_clamp_x, + n_particles_y) + +# Note that the `RectangularShape` puts the first particle half a particle spacing away +# from the boundary, which is correct for fluids, but not for structures. +# We therefore need to pass `place_on_shell=true`. +beam = RectangularShape(particle_spacing, n_particles_per_dimension, + (-length_clamp, 0.0), density=density, place_on_shell=true) + +fixed_particles = setdiff(shape_sampled, beam) + +# structure = union(beam, fixed_particles) + +# Make sure that the kernel support of fluid particles at a boundary is always fully sampled +boundary_layers = 3 + +# Make sure that the kernel support of fluid particles at an open boundary is always +# fully sampled. +# Note: Due to the dynamics at the inlets and outlets of open boundaries, +# it is recommended to use `open_boundary_layers > boundary_layers` +open_boundary_layers = 6 + +fluid_density = 1000.0 +tank = RectangularTank(fluid_particle_spacing, initial_fluid_size, tank_size, fluid_density, + n_layers=boundary_layers, + faces=(false, false, true, true), velocity=initial_velocity) +# fluid = setdiff(tank.fluid, structure) + +open_boundary_size = (fluid_particle_spacing * open_boundary_layers, tank_size[2]) + +min_coords_inlet = (-open_boundary_layers * fluid_particle_spacing, 0.0) +inlet = RectangularTank(fluid_particle_spacing, open_boundary_size, open_boundary_size, + fluid_density, n_layers=boundary_layers, + min_coordinates=min_coords_inlet, + faces=(false, false, true, true)) + +min_coords_outlet = (tank.fluid_size[1], 0.0) +outlet = RectangularTank(fluid_particle_spacing, open_boundary_size, open_boundary_size, + fluid_density, n_layers=boundary_layers, + min_coordinates=min_coords_outlet, + faces=(false, false, true, true)) + + +NDIMS = ndims(tank.fluid) +n_buffer_particles = 10 * tank.n_particles_per_dimension[2]^(NDIMS - 1) + + +structure = union(beam, fixed_particles) +# Move the fin to the center of the tank +structure.coordinates .+= center .+ (fluid_particle_spacing / 2, fluid_particle_spacing / 2) + +fluid = setdiff(tank.fluid, structure) + +n_clamped_particles = nparticles(structure) - nparticles(beam) + +# Movement function +frequency = 1.3 # Hz +amplitude = 0.18 # m +rotation_deg = 25 # degrees +rotation_phase_offset = 0.12 # periods +translation_vector = SVector(0.0, amplitude) +rotation_angle = rotation_deg * pi / 180 + +boundary_motion = OscillatingMotion2D(; frequency, + translation_vector=SVector(0.0, amplitude), + rotation_angle, rotation_center=center, + rotation_phase_offset, ramp_up_tspan=(0.0, 0.5)) + +sound_speed = 40.0 +state_equation = StateEquationCole(; sound_speed, reference_density=fluid_density, + exponent=1, background_pressure=0.0) + +# ========================================================================================== +# ==== Structure +boundary_density_calculator = AdamiPressureExtrapolation() +viscosity_fluid = ViscosityAdami(nu=1e-4) +viscosity_fin = ViscosityAdami(nu=1e-4) + +# For the FSI we need the hydrodynamic masses and densities in the structure boundary model +hydrodynamic_densites = fluid_density * ones(size(structure.density)) +hydrodynamic_masses = hydrodynamic_densites * particle_spacing^2 + +boundary_model_structure = BoundaryModelDummyParticles(hydrodynamic_densites, + hydrodynamic_masses, + state_equation=state_equation, + boundary_density_calculator, + smoothing_kernel, smoothing_length_fluid, + viscosity=viscosity_fin) + +# k_structure = 1.0 +# beta_structure = fluid_particle_spacing / particle_spacing +# boundary_model_structure = BoundaryModelMonaghanKajtar(k_structure, beta_structure, +# particle_spacing, +# hydrodynamic_masses) + +structure_system = TotalLagrangianSPHSystem(structure, smoothing_kernel, smoothing_length_structure, + modulus, poisson_ratio; + n_clamped_particles, #clamped_particles_motion=boundary_motion, + velocity_averaging=nothing, + boundary_model=boundary_model_structure, + viscosity=ArtificialViscosityMonaghan(alpha=0.01), + penalty_force=PenaltyForceGanzenmueller(alpha=0.1)) + +# ========================================================================================== +# ==== Fluid +fluid_density_calculator = ContinuityDensity() +density_diffusion = DensityDiffusionMolteniColagrossi(delta=0.1) +# density_diffusion = DensityDiffusionAntuono(fluid, delta=0.1) + +fluid_system = WeaklyCompressibleSPHSystem(fluid, fluid_density_calculator, + state_equation, smoothing_kernel, + smoothing_length_fluid, viscosity=viscosity_fluid, + density_diffusion=density_diffusion, + shifting_technique=ParticleShiftingTechnique(sound_speed_factor=0.2, v_max_factor=0.0), + # pressure_acceleration=tensile_instability_control, + buffer_size=n_buffer_particles) +# fluid_system = EntropicallyDampedSPHSystem(fluid, smoothing_kernel, smoothing_length, +# sound_speed, viscosity=ViscosityAdami(; nu), +# transport_velocity=TransportVelocityAdami(10 * sound_speed^2 * fluid_density)) + +min_corner = minimum(tank.boundary.coordinates, dims=2) .- fluid_particle_spacing / 2 +max_corner = maximum(tank.boundary.coordinates, dims=2) .+ fluid_particle_spacing / 2 +min_corner = convert.(typeof(fluid_particle_spacing), min_corner) +max_corner = convert.(typeof(fluid_particle_spacing), max_corner) +periodic_box = PeriodicBox(; min_corner, max_corner) +open_boundary_system = nothing +wall = tank.boundary + +# ========================================================================================== +# ==== Boundary +boundary_density_calculator = AdamiPressureExtrapolation() +boundary_model = BoundaryModelDummyParticles(wall.density, wall.mass, + state_equation=state_equation, + boundary_density_calculator, + smoothing_kernel, smoothing_length_fluid) + +boundary_system = WallBoundarySystem(wall, boundary_model) + +# ========================================================================================== +# ==== Simulation +min_corner = minimum(wall.coordinates, dims=2) .- fluid_particle_spacing / 2 +max_corner = maximum(wall.coordinates, dims=2) .+ fluid_particle_spacing / 2 +cell_list = FullGridCellList(; min_corner, max_corner) +neighborhood_search = GridNeighborhoodSearch{2}(; periodic_box, cell_list, + update_strategy=ParallelUpdate()) + +semi = Semidiscretization(fluid_system, boundary_system, open_boundary_system, structure_system; neighborhood_search, + parallelization_backend=PolyesterBackend()) +ode = semidiscretize(semi, tspan) + +info_callback = InfoCallback(interval=100) +prefix = "simplified" +saving_callback = SolutionSavingCallback(dt=0.01, prefix=prefix) + +split_cfl = 1.6 +# SSPRK104 CFL = 2.5, 15k RHS evaluations +# CarpenterKennedy2N54 CFL = 1.6, 11k RHS evaluations +# RK4 CFL = 1.2, 12k RHS evaluations +# VerletLeapfrog CFL = 0.5, 6.75k RHS evaluations +# VelocityVerlet CFL = 0.5, 6.75k RHS evaluations +# DPRKN4 CFL = 1.7, 9k RHS evaluations + +split_integration = SplitIntegrationCallback(CarpenterKennedy2N54(williamson_condition=false), adaptive=false, + stage_coupling=true, + dt=1e-5, # This is overwritten by the stepsize callback + callback=StepsizeCallback(cfl=split_cfl), + maxiters=10^8) + +fluid_cfl = 1.2 +stepsize_callback = StepsizeCallback(cfl=fluid_cfl) + +function total_volume(system::WeaklyCompressibleSPHSystem, data, t) + return sum(data.mass ./ data.density) +end +function total_volume(system, data, t) + return nothing +end +pp_cb = PostprocessCallback(; total_volume, interval=100, + filename="$(prefix)_total_volume", write_file_interval=50) + +function plane_vtk(system, dv_ode, du_ode, v_ode, u_ode, semi, t) + return nothing +end +function plane_vtk(system::WeaklyCompressibleSPHSystem, dv_ode, du_ode, v_ode, u_ode, semi_, t) + resolution = fluid_particle_spacing / 6 + pvd = TrixiParticles.paraview_collection("out/$(prefix)_plane"; append=t > 0) + interpolate_plane_2d_vtk(min_corner, max_corner, resolution, + semi_, semi_.systems[1], v_ode, u_ode, include_wall_velocity=true, + filename="$(prefix)_plane_$(round(Int, t * 1000))", pvd=pvd, t=t) + TrixiParticles.vtk_save(pvd) + return nothing +end +interpolate_cb = PostprocessCallback(; plane_vtk, dt=0.01, filename="plane") + +callbacks = CallbackSet(info_callback, saving_callback, UpdateCallback(), + stepsize_callback, split_integration, pp_cb, interpolate_cb) + +dt_fluid = 1.25e-4 +sol = solve(ode, + # RDPK3SpFSAL35(), + CarpenterKennedy2N54(williamson_condition=false), + dt=dt_fluid, # This is overwritten by the stepsize callback + # reltol=1e-5, abstol=1e-7, + save_everystep=false, callback=callbacks, maxiters=10^8); From 6dec8da986410c04d2d641a70bf8262591ca095d Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Fri, 20 Feb 2026 09:10:35 +0100 Subject: [PATCH 111/134] Add more buffer particles --- examples/fsi/fin_2d.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/fsi/fin_2d.jl b/examples/fsi/fin_2d.jl index 4bb6516559..33ecffc078 100644 --- a/examples/fsi/fin_2d.jl +++ b/examples/fsi/fin_2d.jl @@ -99,7 +99,7 @@ outlet = RectangularTank(fluid_particle_spacing, open_boundary_size, open_bounda NDIMS = ndims(tank.fluid) -n_buffer_particles = 10 * tank.n_particles_per_dimension[2]^(NDIMS - 1) +n_buffer_particles = 20 * tank.n_particles_per_dimension[2]^(NDIMS - 1) # ========================================================================================== # ==== Packing From b8826cb2c7951875611abff74a0e972f52d0b53c Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Fri, 20 Feb 2026 12:56:30 +0100 Subject: [PATCH 112/134] Add code for extracting tip velocity --- examples/fsi/fin_2d.jl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/examples/fsi/fin_2d.jl b/examples/fsi/fin_2d.jl index 33ecffc078..55be612fa1 100644 --- a/examples/fsi/fin_2d.jl +++ b/examples/fsi/fin_2d.jl @@ -350,6 +350,11 @@ split_cfl = 1.0 # VelocityVerlet CFL = 0.5, 6.75k RHS evaluations # DPRKN4 CFL = 1.7, 9k RHS evaluations +# function tip_velocity(system::TotalLagrangianSPHSystem, data, t) +# return data.velocity[2254] +# end +# pp_tip = PostprocessCallback(; tip_velocity, interval=1, +# filename="$(prefix)_tip_velocity", write_file_interval=10_000) split_integration = SplitIntegrationCallback(CarpenterKennedy2N54(williamson_condition=false), adaptive=false, stage_coupling=true, dt=1e-5, # This is overwritten by the stepsize callback From ac3155413a7fe0ba6766ece7a16d63a9bdcea185 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Sat, 21 Feb 2026 15:33:23 +0100 Subject: [PATCH 113/134] Fix CFL and open boundaries in fin example --- examples/fsi/fin_2d.jl | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/examples/fsi/fin_2d.jl b/examples/fsi/fin_2d.jl index 55be612fa1..4602ee6315 100644 --- a/examples/fsi/fin_2d.jl +++ b/examples/fsi/fin_2d.jl @@ -299,7 +299,9 @@ else reference_velocity_out = nothing reference_pressure_out = 0.0 reference_density_out = nothing - boundary_type_out = OutFlow() + # Vortices with negative x-velocity can pass through the outlet, + # which a pure outflow boundary cannot handle. + boundary_type_out = BidirectionalFlow() face_out = ([min_coords_outlet[1], 0.0], [min_coords_outlet[1], tank_size[2]]) outflow = BoundaryZone(; boundary_face=face_out, face_normal=(-flow_direction), open_boundary_layers, density=fluid_density, particle_spacing, @@ -342,7 +344,7 @@ info_callback = InfoCallback(interval=100) prefix = "" saving_callback = SolutionSavingCallback(dt=0.01; prefix) -split_cfl = 1.0 +split_cfl = 1.6 # SSPRK104 CFL = 2.5, 15k RHS evaluations # CarpenterKennedy2N54 CFL = 1.6, 11k RHS evaluations # RK4 CFL = 1.2, 12k RHS evaluations @@ -361,7 +363,7 @@ split_integration = SplitIntegrationCallback(CarpenterKennedy2N54(williamson_con callback=StepsizeCallback(cfl=split_cfl), maxiters=10^8) -fluid_cfl = 0.8 +fluid_cfl = 1.2 stepsize_callback = StepsizeCallback(cfl=fluid_cfl) function total_volume(system::WeaklyCompressibleSPHSystem, data, t) From 0ba432857ddb4c79ee297981055191bdc69fb978 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Wed, 25 Feb 2026 16:38:14 +0100 Subject: [PATCH 114/134] Fix initial condition for inlet/outlet --- examples/fsi/fin_2d.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/fsi/fin_2d.jl b/examples/fsi/fin_2d.jl index 4602ee6315..5a17a7ee65 100644 --- a/examples/fsi/fin_2d.jl +++ b/examples/fsi/fin_2d.jl @@ -89,12 +89,14 @@ min_coords_inlet = (-open_boundary_layers * fluid_particle_spacing, 0.0) inlet = RectangularTank(fluid_particle_spacing, open_boundary_size, open_boundary_size, fluid_density, n_layers=boundary_layers, min_coordinates=min_coords_inlet, + velocity=initial_velocity, faces=(false, false, true, true)) min_coords_outlet = (tank.fluid_size[1], 0.0) outlet = RectangularTank(fluid_particle_spacing, open_boundary_size, open_boundary_size, fluid_density, n_layers=boundary_layers, min_coordinates=min_coords_outlet, + velocity=initial_velocity, faces=(false, false, true, true)) From 1d8eea9dc18bf911e06475167fdbc4d9ae43ac5e Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Fri, 27 Feb 2026 15:04:27 +0100 Subject: [PATCH 115/134] Quick fix for making the `InitialCondition` type safer --- src/general/initial_condition.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/general/initial_condition.jl b/src/general/initial_condition.jl index b78ae8013f..d49553bf47 100644 --- a/src/general/initial_condition.jl +++ b/src/general/initial_condition.jl @@ -95,7 +95,7 @@ initial_condition = InitialCondition(; coordinates, velocity=x -> 2x, mass=1.0, └──────────────────────────────────────────────────────────────────────────────────────────────────┘ ``` """ -struct InitialCondition{ELTYPE, C, MATRIX, VECTOR} +struct InitialCondition{ELTYPE, C, MATRIX <: AbstractArray{ELTYPE}, VECTOR <: AbstractVector{ELTYPE}} particle_spacing :: ELTYPE coordinates :: C # Array{coordinates_eltype, 2} velocity :: MATRIX # Array{ELTYPE, 2} From 8b465c34e952bc382433e2073ad5ab9d2712830b Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Fri, 27 Feb 2026 15:07:02 +0100 Subject: [PATCH 116/134] Fix type of prescribed motion --- src/schemes/boundary/prescribed_motion.jl | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/schemes/boundary/prescribed_motion.jl b/src/schemes/boundary/prescribed_motion.jl index 6e5fff7272..4ea5b19ef2 100644 --- a/src/schemes/boundary/prescribed_motion.jl +++ b/src/schemes/boundary/prescribed_motion.jl @@ -83,9 +83,10 @@ function initialize_prescribed_motion!(::Nothing, initial_condition, end function (prescribed_motion::PrescribedMotion)(coordinates, velocity, acceleration, - ismoving, system, semi, t) + ismoving, system, semi, t_) (; movement_function, is_moving, moving_particles) = prescribed_motion + t = convert(Float64, t_) ismoving[] = is_moving(t) is_moving(t) || return nothing @@ -160,19 +161,22 @@ PrescribedMotion{...} function OscillatingMotion2D(; frequency, translation_vector, rotation_angle, rotation_center, rotation_phase_offset=0, tspan=(-Inf, Inf), ramp_up_tspan=(0.0, 0.0), moving_particles=nothing) - translation_vector_ = SVector{2}(translation_vector) - rotation_center_ = SVector{2}(rotation_center) + translation_vector_ = SVector{2, Float64}(translation_vector) + rotation_center_ = SVector{2, Float64}(rotation_center) + frequency_ = convert(Float64, frequency) + rotation_angle_ = convert(Float64, rotation_angle) + rotation_phase_offset_ = convert(Float64, rotation_phase_offset) @inline function movement_function(x, t) if isfinite(tspan[1]) t = t - tspan[1] end - sin_scaled = sin(frequency * 2pi * t) + sin_scaled = sin(frequency_ * 2pi * t) translation = sin_scaled * translation_vector_ x_centered = x .- rotation_center_ - sin_scaled_offset = sin(2pi * (frequency * t - rotation_phase_offset)) - angle = rotation_angle * sin_scaled_offset + sin_scaled_offset = sin(2pi * (frequency_ * t - rotation_phase_offset_)) + angle = rotation_angle_ * sin_scaled_offset rotated = SVector(x_centered[1] * cos(angle) - x_centered[2] * sin(angle), x_centered[1] * sin(angle) + x_centered[2] * cos(angle)) From 1d1aa4190b75d2a8e766b0b2f40a8c2f19a2a312 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Fri, 27 Feb 2026 15:09:05 +0100 Subject: [PATCH 117/134] Fix simplified fin example --- examples/fsi/fin_2d_simplified.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/fsi/fin_2d_simplified.jl b/examples/fsi/fin_2d_simplified.jl index 18134e9b1d..2234fe1df8 100644 --- a/examples/fsi/fin_2d_simplified.jl +++ b/examples/fsi/fin_2d_simplified.jl @@ -147,7 +147,7 @@ structure_system = TotalLagrangianSPHSystem(structure, smoothing_kernel, smoothi n_clamped_particles, #clamped_particles_motion=boundary_motion, velocity_averaging=nothing, boundary_model=boundary_model_structure, - viscosity=ArtificialViscosityMonaghan(alpha=0.01), + viscosity=ArtificialViscosityMonaghan(alpha=0.1), penalty_force=PenaltyForceGanzenmueller(alpha=0.1)) # ========================================================================================== @@ -161,7 +161,7 @@ fluid_system = WeaklyCompressibleSPHSystem(fluid, fluid_density_calculator, smoothing_length_fluid, viscosity=viscosity_fluid, density_diffusion=density_diffusion, shifting_technique=ParticleShiftingTechnique(sound_speed_factor=0.2, v_max_factor=0.0), - # pressure_acceleration=tensile_instability_control, + pressure_acceleration=tensile_instability_control, buffer_size=n_buffer_particles) # fluid_system = EntropicallyDampedSPHSystem(fluid, smoothing_kernel, smoothing_length, # sound_speed, viscosity=ViscosityAdami(; nu), From d0cee87e1e297d2caaee4b91f2021a72744c8234 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Fri, 27 Feb 2026 15:09:41 +0100 Subject: [PATCH 118/134] Make FP64 coordinates work with the fin --- examples/fsi/fin_2d.jl | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/examples/fsi/fin_2d.jl b/examples/fsi/fin_2d.jl index 5a17a7ee65..49b8a2ecfb 100644 --- a/examples/fsi/fin_2d.jl +++ b/examples/fsi/fin_2d.jl @@ -2,6 +2,11 @@ using TrixiParticles using OrdinaryDiffEqLowStorageRK using OrdinaryDiffEqSymplecticRK +function convert_ic(ic, T) + return InitialCondition{ndims(ic)}(ic.coordinates, ic.velocity, ic.mass, ic.density, + ic.pressure, T(ic.particle_spacing)) +end + # ========================================================================================== # ==== Resolution n_particles_y = 4 @@ -52,6 +57,7 @@ point_in_geometry_algorithm = WindingNumberJacobson(; geometry, # Returns `InitialCondition` shape_sampled = ComplexShape(geometry; particle_spacing, density=density, grid_offset=center, point_in_geometry_algorithm) +shape_sampled = TrixiParticles.@set shape_sampled.coordinates = Float64.(shape_sampled.coordinates) # Beam and clamped particles length_clamp = round(Int, 0.15 / particle_spacing) * particle_spacing # m @@ -113,6 +119,7 @@ if packing boundary_packing = sample_boundary(foot_sdf; boundary_density=density, boundary_thickness=4 * particle_spacing) + boundary_packing = TrixiParticles.@set boundary_packing.coordinates = Float64.(boundary_packing.coordinates) boundary_packing = setdiff(boundary_packing, beam) background_pressure = 1.0 From 58523d10ad03f53d15cdc1ec3aa95601fe599350 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Fri, 27 Feb 2026 15:10:02 +0100 Subject: [PATCH 119/134] Fix fin open boundaries and add velocity averaging --- examples/fsi/fin_2d.jl | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/examples/fsi/fin_2d.jl b/examples/fsi/fin_2d.jl index 49b8a2ecfb..bc3a925ce8 100644 --- a/examples/fsi/fin_2d.jl +++ b/examples/fsi/fin_2d.jl @@ -81,7 +81,7 @@ boundary_layers = 3 # fully sampled. # Note: Due to the dynamics at the inlets and outlets of open boundaries, # it is recommended to use `open_boundary_layers > boundary_layers` -open_boundary_layers = 6 +open_boundary_layers = 10 fluid_density = 1000.0 tank = RectangularTank(fluid_particle_spacing, initial_fluid_size, tank_size, fluid_density, @@ -255,7 +255,7 @@ structure_system = TotalLagrangianSPHSystem(structure, smoothing_kernel, smoothi modulus, poisson_ratio; n_clamped_particles, clamped_particles_motion=boundary_motion, boundary_model=boundary_model_structure, - velocity_averaging=nothing, + velocity_averaging=TrixiParticles.VelocityAveraging(5e-4), viscosity=ArtificialViscosityMonaghan(alpha=0.1), penalty_force=PenaltyForceGanzenmueller(alpha=0.1)) @@ -293,7 +293,7 @@ else open_boundary_model = BoundaryModelDynamicalPressureZhang() # open_boundary_model = BoundaryModelMirroringTafuni(; mirror_method=ZerothOrderMirroring()) reference_velocity_in = SVector(1.0, 0.0) - reference_pressure_in = nothing + reference_pressure_in = 0.0 reference_density_in = nothing boundary_type_in = InFlow() face_in = ([0.0, 0.0], [0.0, tank_size[2]]) @@ -305,12 +305,10 @@ else reference_velocity=reference_velocity_in, initial_condition=inlet.fluid, boundary_type=boundary_type_in) - reference_velocity_out = nothing - reference_pressure_out = 0.0 + reference_velocity_out = SVector(1.0, 0.0) + reference_pressure_out = nothing reference_density_out = nothing - # Vortices with negative x-velocity can pass through the outlet, - # which a pure outflow boundary cannot handle. - boundary_type_out = BidirectionalFlow() + boundary_type_out = OutFlow() face_out = ([min_coords_outlet[1], 0.0], [min_coords_outlet[1], tank_size[2]]) outflow = BoundaryZone(; boundary_face=face_out, face_normal=(-flow_direction), open_boundary_layers, density=fluid_density, particle_spacing, @@ -321,8 +319,7 @@ else open_boundary_system = OpenBoundarySystem(inflow, outflow; fluid_system, boundary_model=open_boundary_model, - buffer_size=n_buffer_particles, - shifting_technique=nothing) + buffer_size=n_buffer_particles) wall = union(tank.boundary, inlet.boundary, outlet.boundary) min_corner = minimum(wall.coordinates, dims=2) .- 5 * fluid_particle_spacing From b3e9bc0c06cafb80958ff122a0c431e73f35be91 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Fri, 8 May 2026 17:15:10 +0200 Subject: [PATCH 120/134] Apply API changes --- examples/fsi/fin_2d.jl | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/examples/fsi/fin_2d.jl b/examples/fsi/fin_2d.jl index bc3a925ce8..11fa01e8e3 100644 --- a/examples/fsi/fin_2d.jl +++ b/examples/fsi/fin_2d.jl @@ -158,7 +158,7 @@ if packing packed_foot.coordinates .+= center beam.coordinates .+= center - structure = union(beam, packed_foot) + structure = union(packed_foot, beam) fluid = setdiff(tank.fluid, structure) # Pack the fluid against the fin and the tank boundary @@ -202,7 +202,7 @@ if packing fluid = InitialCondition(sol_packing, fluid_packing_system, semi_packing) fluid = union(fluid, fixed_fluid) else - structure = union(beam, fixed_particles) + structure = union(fixed_particles, beam) # Move the fin to the center of the tank structure.coordinates .+= center @@ -251,11 +251,12 @@ boundary_model_structure = BoundaryModelDummyParticles(hydrodynamic_densites, # particle_spacing, # hydrodynamic_masses) -structure_system = TotalLagrangianSPHSystem(structure, smoothing_kernel, smoothing_length_structure, - modulus, poisson_ratio; - n_clamped_particles, clamped_particles_motion=boundary_motion, +structure_system = TotalLagrangianSPHSystem(structure; smoothing_kernel, smoothing_length=smoothing_length_structure, + young_modulus=modulus, poisson_ratio, + clamped_particles=1:n_clamped_particles, + clamped_particles_motion=boundary_motion, boundary_model=boundary_model_structure, - velocity_averaging=TrixiParticles.VelocityAveraging(5e-4), + velocity_averaging=TrixiParticles.VelocityAveraging(time_constant=5e-4), viscosity=ArtificialViscosityMonaghan(alpha=0.1), penalty_force=PenaltyForceGanzenmueller(alpha=0.1)) @@ -265,10 +266,11 @@ fluid_density_calculator = ContinuityDensity() # density_diffusion = DensityDiffusionMolteniColagrossi(delta=0.1) density_diffusion = DensityDiffusionAntuono(fluid, delta=0.1) -fluid_system = WeaklyCompressibleSPHSystem(fluid, fluid_density_calculator, +fluid_system = WeaklyCompressibleSPHSystem(fluid; density_calculator=fluid_density_calculator, state_equation, smoothing_kernel, - smoothing_length_fluid, viscosity=viscosity_fluid, - density_diffusion=density_diffusion, + smoothing_length=smoothing_length_fluid, + viscosity=viscosity_fluid, + density_diffusion, shifting_technique=ParticleShiftingTechnique(sound_speed_factor=0.2, v_max_factor=0.0), pressure_acceleration=tensile_instability_control, buffer_size=n_buffer_particles) From 01cfba483d8de80e10f54885eb61acad7a480c63 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Wed, 13 May 2026 11:56:49 +0200 Subject: [PATCH 121/134] Add PID to InfoCallback --- examples/fsi/fin_2d.jl | 2 +- src/callbacks/info.jl | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/examples/fsi/fin_2d.jl b/examples/fsi/fin_2d.jl index 11fa01e8e3..1406c8b522 100644 --- a/examples/fsi/fin_2d.jl +++ b/examples/fsi/fin_2d.jl @@ -399,7 +399,7 @@ interpolate_cb = PostprocessCallback(; plane_vtk, dt=0.01, filename="plane") callbacks = CallbackSet(info_callback, saving_callback, stepsize_callback, split_integration, pp_cb, interpolate_cb, - UpdateCallback()) + UpdateCallback(), SortingCallback(interval=100_000)) dt_fluid = 1.25e-4 sol = solve(ode, diff --git a/src/callbacks/info.jl b/src/callbacks/info.jl index 45c60a4a79..e8ad9b37b0 100644 --- a/src/callbacks/info.jl +++ b/src/callbacks/info.jl @@ -1,5 +1,7 @@ mutable struct InfoCallback start_time::Float64 + last_performance_print_time::Float64 + ncalls_at_last_print::Int interval::Int end @@ -33,7 +35,7 @@ beginning of a simulation and then resets the timer. When the returned callback directly, the current timer values are shown. """ function InfoCallback(; interval=0, reset_threads=true) - info_callback = InfoCallback(0.0, interval) + info_callback = InfoCallback(0.0, 0.0, 0, interval) function initialize(cb, u, t, integrator) initialize_info_callback(cb, u, t, integrator; @@ -65,6 +67,17 @@ function (info_callback::InfoCallback)(integrator) integrator.stats.naccept, integrator.dt, t, sim_time_percentage), 71) * @sprintf("│ run time: %.4e s", runtime_absolute)) + + if condition_integrator_interval(integrator, 1 * info_callback.interval) + runtime_since_last_print = (time_ns() - info_callback.last_performance_print_time) + info_callback.last_performance_print_time = time_ns() + n_calls = integrator.stats.nf - info_callback.ncalls_at_last_print + info_callback.ncalls_at_last_print = integrator.stats.nf + pid = runtime_since_last_print / n_calls / sum(nparticles.(integrator.p.semi.systems)) + println("─"^100) + println(@sprintf("time/particle/rhs: %.4e ns", pid)) + println("─"^100) + end end # This callback only reports progress and does not change the result of the right-hand side. @@ -143,6 +156,8 @@ function initialize_info_callback(discrete_callback, u, t, integrator; # Save current time as start_time info_callback.start_time = time_ns() + info_callback.last_performance_print_time = time_ns() + info_callback.ncalls_at_last_print = 0 return nothing end From e54bac5a86e1d27c407fbed9a623d6bb24a5f26a Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Fri, 15 May 2026 10:28:58 +0200 Subject: [PATCH 122/134] Fix Antuono in fin example file --- examples/fsi/fin_2d.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/fsi/fin_2d.jl b/examples/fsi/fin_2d.jl index 1406c8b522..4e20e8d340 100644 --- a/examples/fsi/fin_2d.jl +++ b/examples/fsi/fin_2d.jl @@ -264,7 +264,7 @@ structure_system = TotalLagrangianSPHSystem(structure; smoothing_kernel, smoothi # ==== Fluid fluid_density_calculator = ContinuityDensity() # density_diffusion = DensityDiffusionMolteniColagrossi(delta=0.1) -density_diffusion = DensityDiffusionAntuono(fluid, delta=0.1) +density_diffusion = DensityDiffusionAntuono(delta=0.1) fluid_system = WeaklyCompressibleSPHSystem(fluid; density_calculator=fluid_density_calculator, state_equation, smoothing_kernel, From ee960bbe34653ff2948626ca9af0f3243b5e02d0 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Fri, 15 May 2026 10:29:48 +0200 Subject: [PATCH 123/134] Make Antuono work on GPUs --- src/schemes/boundary/open_boundary/system.jl | 4 ++++ src/schemes/fluid/fluid.jl | 2 +- src/schemes/fluid/weakly_compressible_sph/system.jl | 5 +++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/schemes/boundary/open_boundary/system.jl b/src/schemes/boundary/open_boundary/system.jl index 4348f7df6f..5e72ae310e 100644 --- a/src/schemes/boundary/open_boundary/system.jl +++ b/src/schemes/boundary/open_boundary/system.jl @@ -135,6 +135,10 @@ function OpenBoundarySystem(boundary_zones::Union{BoundaryZone, Nothing}...; calculate_flow_rate, cache) end +function Base.:(==)(system1::OpenBoundarySystem, system2::OpenBoundarySystem) + return system1.mass === system2.mass +end + function initialize!(system::OpenBoundarySystem, semi) (; boundary_zones) = system diff --git a/src/schemes/fluid/fluid.jl b/src/schemes/fluid/fluid.jl index 53293df2c6..97e9bf08b3 100644 --- a/src/schemes/fluid/fluid.jl +++ b/src/schemes/fluid/fluid.jl @@ -176,7 +176,7 @@ end # Artificial density diffusion should only be applied to systems representing a fluid # with the same physical properties i.e. density and viscosity. # TODO: shouldn't be applied to particles on the interface (depends on PR #539) - if particle_system === neighbor_system + if particle_system == neighbor_system density_diffusion!(drho_particle, density_diffusion(particle_system), particle_system, particle, neighbor, pos_diff, distance, m_b, rho_a, rho_b, grad_kernel) diff --git a/src/schemes/fluid/weakly_compressible_sph/system.jl b/src/schemes/fluid/weakly_compressible_sph/system.jl index 2e5474a33c..00eb47409a 100644 --- a/src/schemes/fluid/weakly_compressible_sph/system.jl +++ b/src/schemes/fluid/weakly_compressible_sph/system.jl @@ -173,6 +173,11 @@ function WeaklyCompressibleSPHSystem(initial_condition; smoothing_kernel, cache) end +@inline function Base.:(==)(system1::WeaklyCompressibleSPHSystem, + system2::WeaklyCompressibleSPHSystem) + return system1.mass === system2.mass +end + function Base.show(io::IO, system::WeaklyCompressibleSPHSystem) @nospecialize system # reduce precompilation time From 4e98d8a0d10697336a5dea10e1990980250abb9a Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Fri, 15 May 2026 17:54:03 +0200 Subject: [PATCH 124/134] Print number of split integration time steps in summary --- src/callbacks/info.jl | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/callbacks/info.jl b/src/callbacks/info.jl index e8ad9b37b0..3134247144 100644 --- a/src/callbacks/info.jl +++ b/src/callbacks/info.jl @@ -271,9 +271,16 @@ end function print_summary(integrator) println("─"^100) - println("Trixi simulation finished. Final time: ", integrator.t, - " Time steps: ", integrator.stats.naccept, " (accepted), ", - integrator.iter, " (total)") + # Print the final time as string to let Julia decide on the formatting + @printf(" %-31s %10s\n", "Final time:", string(integrator.t)) + @printf(" %-31s %10d (accepted) %10d (total)\n", + "Time steps:", integrator.stats.naccept, integrator.iter) + if !isnothing(integrator.p.split_integration_data) + @printf(" %-31s %10d (accepted) %10d (total)\n", + "Split integration time steps:", + integrator.p.split_integration_data.integrator.stats.naccept, + integrator.p.split_integration_data.integrator.iter) + end println("─"^100) println() From c9654a5f09a12829c784dd08532648ce2fc76097 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Mon, 18 May 2026 12:33:27 +0200 Subject: [PATCH 125/134] Sort more frequently --- examples/fsi/fin_2d.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/fsi/fin_2d.jl b/examples/fsi/fin_2d.jl index 4e20e8d340..877c1cea88 100644 --- a/examples/fsi/fin_2d.jl +++ b/examples/fsi/fin_2d.jl @@ -399,7 +399,7 @@ interpolate_cb = PostprocessCallback(; plane_vtk, dt=0.01, filename="plane") callbacks = CallbackSet(info_callback, saving_callback, stepsize_callback, split_integration, pp_cb, interpolate_cb, - UpdateCallback(), SortingCallback(interval=100_000)) + UpdateCallback(), SortingCallback(interval=10_000)) dt_fluid = 1.25e-4 sol = solve(ode, From 9b3b5c528a4079fb8d6df18335cf8c892a4d445c Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Thu, 21 May 2026 16:57:50 +0200 Subject: [PATCH 126/134] Fix packing for higher resolution (1mm thick fin) --- examples/fsi/fin_2d.jl | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/examples/fsi/fin_2d.jl b/examples/fsi/fin_2d.jl index 877c1cea88..051421ce41 100644 --- a/examples/fsi/fin_2d.jl +++ b/examples/fsi/fin_2d.jl @@ -136,9 +136,10 @@ if packing min_corner = minimum(tank.boundary.coordinates, dims=2) .- fluid_particle_spacing / 2 max_corner = maximum(tank.boundary.coordinates, dims=2) .+ fluid_particle_spacing / 2 - periodic_box = PeriodicBox(; min_corner, max_corner) + min_corner .-= center + max_corner .-= center cell_list = FullGridCellList(; min_corner, max_corner) - neighborhood_search = GridNeighborhoodSearch{2}(; periodic_box, cell_list, update_strategy=ParallelUpdate()) + neighborhood_search = GridNeighborhoodSearch{2}(; cell_list, update_strategy=ParallelUpdate()) semi_packing = Semidiscretization(foot_packing_system, fluid_packing_system, blade_packing_system; neighborhood_search) @@ -146,9 +147,10 @@ if packing ode_packing = semidiscretize(semi_packing, (0.0, 10.0)) sol_packing = solve(ode_packing, RDPK3SpFSAL35(); + abstol=1e-8, save_everystep=false, callback=CallbackSet(InfoCallback(interval=50), - # SolutionSavingCallback(interval=50, prefix="packing"), + # SolutionSavingCallback(interval=50, prefix="packing_foot"), UpdateCallback()), dtmax=1e-2) @@ -158,7 +160,9 @@ if packing packed_foot.coordinates .+= center beam.coordinates .+= center - structure = union(packed_foot, beam) + # `union(packed_foot, beam)`, but when particles are too close together, keep the ones + # from `beam` instead of `packed_foot` to ensure that the blade doesn't have holes. + structure = union(setdiff(packed_foot, beam), beam) fluid = setdiff(tank.fluid, structure) # Pack the fluid against the fin and the tank boundary @@ -187,6 +191,11 @@ if packing fixed_packing_system = ParticlePackingSystem(fixed_union; smoothing_length=smoothing_length_packing, fixed_system=true, signed_distance_field=nothing, background_pressure) + min_corner = minimum(tank.boundary.coordinates, dims=2) .- fluid_particle_spacing / 2 + max_corner = maximum(tank.boundary.coordinates, dims=2) .+ fluid_particle_spacing / 2 + cell_list = FullGridCellList(; min_corner, max_corner) + neighborhood_search = GridNeighborhoodSearch{2}(; cell_list, update_strategy=ParallelUpdate()) + semi_packing = Semidiscretization(fluid_packing_system, fixed_packing_system; neighborhood_search) @@ -197,6 +206,7 @@ if packing callback=CallbackSet(InfoCallback(interval=50), # SolutionSavingCallback(interval=50, prefix="packing"), UpdateCallback()), + abstol=1e-8, dtmax=1e-2) fluid = InitialCondition(sol_packing, fluid_packing_system, semi_packing) From 1e05957e9460bca083616e0a0237b6b3626aa487 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Thu, 21 May 2026 16:58:05 +0200 Subject: [PATCH 127/134] Double structure viscosity to make 1mm fin stable --- examples/fsi/fin_2d.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/fsi/fin_2d.jl b/examples/fsi/fin_2d.jl index 051421ce41..967d4b9d98 100644 --- a/examples/fsi/fin_2d.jl +++ b/examples/fsi/fin_2d.jl @@ -267,7 +267,7 @@ structure_system = TotalLagrangianSPHSystem(structure; smoothing_kernel, smoothi clamped_particles_motion=boundary_motion, boundary_model=boundary_model_structure, velocity_averaging=TrixiParticles.VelocityAveraging(time_constant=5e-4), - viscosity=ArtificialViscosityMonaghan(alpha=0.1), + viscosity=ArtificialViscosityMonaghan(alpha=0.2), penalty_force=PenaltyForceGanzenmueller(alpha=0.1)) # ========================================================================================== From 85d0535f30e5c437daaffb55bf2658d6fdd4f7bc Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Thu, 21 May 2026 16:58:29 +0200 Subject: [PATCH 128/134] Update split CFL to make 1mm fin stable --- examples/fsi/fin_2d.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/fsi/fin_2d.jl b/examples/fsi/fin_2d.jl index 967d4b9d98..614f513cce 100644 --- a/examples/fsi/fin_2d.jl +++ b/examples/fsi/fin_2d.jl @@ -362,7 +362,7 @@ info_callback = InfoCallback(interval=100) prefix = "" saving_callback = SolutionSavingCallback(dt=0.01; prefix) -split_cfl = 1.6 +split_cfl = 1.5 # SSPRK104 CFL = 2.5, 15k RHS evaluations # CarpenterKennedy2N54 CFL = 1.6, 11k RHS evaluations # RK4 CFL = 1.2, 12k RHS evaluations From af2e7180ae3f122335f54fb1c4704642848e72aa Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Sat, 23 May 2026 20:40:08 +0200 Subject: [PATCH 129/134] Use Molteni-Colagrossi density diffusion by default --- examples/fsi/fin_2d.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/fsi/fin_2d.jl b/examples/fsi/fin_2d.jl index 614f513cce..06ff920ed1 100644 --- a/examples/fsi/fin_2d.jl +++ b/examples/fsi/fin_2d.jl @@ -261,20 +261,20 @@ boundary_model_structure = BoundaryModelDummyParticles(hydrodynamic_densites, # particle_spacing, # hydrodynamic_masses) +viscosity_structure = ArtificialViscosityMonaghan(alpha=0.2) structure_system = TotalLagrangianSPHSystem(structure; smoothing_kernel, smoothing_length=smoothing_length_structure, young_modulus=modulus, poisson_ratio, clamped_particles=1:n_clamped_particles, clamped_particles_motion=boundary_motion, boundary_model=boundary_model_structure, velocity_averaging=TrixiParticles.VelocityAveraging(time_constant=5e-4), - viscosity=ArtificialViscosityMonaghan(alpha=0.2), + viscosity=viscosity_structure, penalty_force=PenaltyForceGanzenmueller(alpha=0.1)) # ========================================================================================== # ==== Fluid fluid_density_calculator = ContinuityDensity() -# density_diffusion = DensityDiffusionMolteniColagrossi(delta=0.1) -density_diffusion = DensityDiffusionAntuono(delta=0.1) +density_diffusion = DensityDiffusionMolteniColagrossi(delta=0.1) fluid_system = WeaklyCompressibleSPHSystem(fluid; density_calculator=fluid_density_calculator, state_equation, smoothing_kernel, From d8dd145f1ad057937df0a8c7241e5b4d38ca1eb0 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Tue, 16 Jun 2026 12:04:08 +0200 Subject: [PATCH 130/134] Add work and thrust calculator --- examples/fsi/fin_2d.jl | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/examples/fsi/fin_2d.jl b/examples/fsi/fin_2d.jl index 06ff920ed1..da7d6fd400 100644 --- a/examples/fsi/fin_2d.jl +++ b/examples/fsi/fin_2d.jl @@ -407,8 +407,15 @@ function plane_vtk(system::WeaklyCompressibleSPHSystem, dv_ode, du_ode, v_ode, u end interpolate_cb = PostprocessCallback(; plane_vtk, dt=0.01, filename="plane") +mechanical_work_calculator = MechanicalWorkCalculator(semi.systems[4], semi) +thrust_calculator = ThrustCalculator(semi.systems[4], semi, direction=SVector(1.0, 0.0)) +calculator_cb = PostprocessCallback(; mechanical_work_calculator, thrust_calculator, + interval=100, filename="$(prefix)_efficiency", + write_file_interval=1000) + callbacks = CallbackSet(info_callback, saving_callback, stepsize_callback, split_integration, pp_cb, interpolate_cb, + calculator_cb, UpdateCallback(), SortingCallback(interval=10_000)) dt_fluid = 1.25e-4 From df4b370f72c3c310be962151f588216ba8e7e628 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Tue, 16 Jun 2026 17:44:27 +0200 Subject: [PATCH 131/134] Add realistic data for Hyper Bifins X (untested) --- examples/fsi/fin_2d.jl | 13 +- .../data/{fin.dxf => hyper_bifins_x.dxf} | 1956 ++--------------- 2 files changed, 215 insertions(+), 1754 deletions(-) rename examples/preprocessing/data/{fin.dxf => hyper_bifins_x.dxf} (63%) diff --git a/examples/fsi/fin_2d.jl b/examples/fsi/fin_2d.jl index da7d6fd400..c8b4326b86 100644 --- a/examples/fsi/fin_2d.jl +++ b/examples/fsi/fin_2d.jl @@ -15,7 +15,7 @@ n_particles_y = 4 # ==== Experiment Setup tspan = (0.0, 2.0) -fin_length = 0.6 +fin_length = 0.502 fin_thickness = 30e-3 real_thickness = 1e-3 real_modulus = 125e9 @@ -26,10 +26,11 @@ modulus = 12 * (1 - poisson_ratio^2) * flexural_rigidity / fin_thickness^3 fiber_volume_fraction = 0.6 fiber_density = 1800.0 epoxy_density = 1250.0 -density = fiber_volume_fraction * fiber_density + +real_density = fiber_volume_fraction * fiber_density + (1 - fiber_volume_fraction) * epoxy_density - -clamp_radius = 0.05 +# Scale the density with the thickness ratio to keep the mass of the fin constant +# when changing the thickness. +density = real_density * (fin_thickness / real_thickness) tank_size = (2.0, 1.0) center = (tank_size[2] / 2, tank_size[2] / 2) @@ -45,7 +46,7 @@ smoothing_length_structure = sqrt(2) * particle_spacing smoothing_length_fluid = 1.5 * fluid_particle_spacing smoothing_kernel = WendlandC2Kernel{2}() -file = joinpath(examples_dir(), "preprocessing", "data", "fin.dxf") +file = joinpath(examples_dir(), "preprocessing", "data", "hyper_bifins_x.dxf") geometry = load_geometry(file) # trixi2vtk(geometry) @@ -410,7 +411,7 @@ interpolate_cb = PostprocessCallback(; plane_vtk, dt=0.01, filename="plane") mechanical_work_calculator = MechanicalWorkCalculator(semi.systems[4], semi) thrust_calculator = ThrustCalculator(semi.systems[4], semi, direction=SVector(1.0, 0.0)) calculator_cb = PostprocessCallback(; mechanical_work_calculator, thrust_calculator, - interval=100, filename="$(prefix)_efficiency", + interval=1, filename="$(prefix)_efficiency", write_file_interval=1000) callbacks = CallbackSet(info_callback, saving_callback, diff --git a/examples/preprocessing/data/fin.dxf b/examples/preprocessing/data/hyper_bifins_x.dxf similarity index 63% rename from examples/preprocessing/data/fin.dxf rename to examples/preprocessing/data/hyper_bifins_x.dxf index 3f56143d5a..87ffef8728 100644 --- a/examples/preprocessing/data/fin.dxf +++ b/examples/preprocessing/data/hyper_bifins_x.dxf @@ -489,7 +489,7 @@ $SKPOLY 9 $TDCREATE 40 -2461046.8383217594 +2461208.724513889 9 $TDUCREATE 40 @@ -497,7 +497,7 @@ $TDUCREATE 9 $TDUPDATE 40 -2461046.8383217594 +2461208.724513889 9 $TDUUPDATE 40 @@ -549,7 +549,7 @@ $SPLINESEGS 9 $HANDSEED 5 -DA +94 9 $SURFTAB1 70 @@ -929,11 +929,11 @@ $PSTYLEMODE 9 $FINGERPRINTGUID 2 -BD1C87EE-EA69-11F0-BD04-A088C2115B2C +4D9D8938-6997-11F1-A0CF-A088C2115B2C 9 $VERSIONGUID 2 -BD1D174A-EA69-11F0-BD04-A088C2115B2C +4D9DEE3C-6997-11F1-A0CF-A088C2115B2C 9 $EXTNAMES 290 @@ -1323,9 +1323,9 @@ ISM 0 CLASS 1 -LAYOUT +ACDBPLACEHOLDER 2 -AcDbLayout +AcDbPlaceHolder 3 ObjectDBX Classes 90 @@ -1339,9 +1339,9 @@ ObjectDBX Classes 0 CLASS 1 -ACDBPLACEHOLDER +LAYOUT 2 -AcDbPlaceHolder +AcDbLayout 3 ObjectDBX Classes 90 @@ -1701,7 +1701,7 @@ ACAD 0 APPID 5 -D9 +93 330 3 100 @@ -2047,7 +2047,7 @@ AcDb2dVertex 10 0.0 20 -5.551115123125783e-17 +0.0 30 0.0 70 @@ -2067,9 +2067,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.0010647730582502013 +-0.005623843252542472 20 --2.8770736736127844e-05 +4.8636960090447824e-05 30 0.0 70 @@ -2089,9 +2089,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.0034971934090743395 +-0.012585488133344366 20 --4.953574980059994e-05 +9.214060349372237e-05 30 0.0 70 @@ -2111,9 +2111,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.007027977637432459 +-0.02061691015601784 20 --6.326524879191053e-05 +0.00012974617782760488 30 0.0 70 @@ -2133,9 +2133,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.011387842328284381 +-0.02945008483417531 20 --7.092944330866491e-05 +0.00016068893070988926 30 0.0 70 @@ -2155,9 +2155,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.01630750406658993 +-0.03881698768142893 20 --7.34985429493018e-05 +0.00018420410975834403 30 0.0 70 @@ -2177,9 +2177,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.02151767943730898 +-0.048449594211391014 20 --7.19427573124265e-05 +0.00019952696259076315 30 0.0 70 @@ -2199,9 +2199,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.026749085025401576 +-0.05807987993767382 20 --6.723229599658875e-05 +0.0002058927368249151 30 0.0 70 @@ -2221,9 +2221,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.031732437415827486 +-0.06743982037388971 20 --6.0337368600282826e-05 +0.00020253668007860656 30 0.0 70 @@ -2243,9 +2243,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.03619845319354664 +-0.0762613910336508 20 --5.222818472205848e-05 +0.00018869403996961873 30 0.0 70 @@ -2265,9 +2265,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.03987784894351887 +-0.0842765674305695 20 --4.387495396040997e-05 +0.00016360006411573287 30 0.0 70 @@ -2287,9 +2287,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.04378446456114665 +-0.091217325078258 20 --0.0008783586002438781 +0.00012649000013471745 30 0.0 70 @@ -2309,9 +2309,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.048103522430103185 +-0.09681563949032863 20 --0.0025049262890193824 +7.659909564437914e-05 30 0.0 70 @@ -2331,9 +2331,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.051718721680916624 +-0.10080348618039366 20 --0.003948651160244676 +1.3162598262486442e-05 30 0.0 70 @@ -2353,9 +2353,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.0549385925607315 +-0.10367157245296806 20 --0.0052322536218777915 +-1.17394308284186e-05 30 0.0 70 @@ -2375,9 +2375,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.05807166531669217 +-0.10800875657294529 20 --0.006378454081876428 +-0.00020302270660931183 30 0.0 70 @@ -2397,9 +2397,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.061532460806013545 +-0.11378660650741865 20 --0.007673859629111657 +-0.0005710572626687967 30 0.0 70 @@ -2419,9 +2419,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.06516986230184571 +-0.12080334462153497 20 --0.009171395348674838 +-0.001096656544127838 30 0.0 70 @@ -2441,9 +2441,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.06880726379767793 +-0.1288571932804413 20 --0.010668931068237963 +-0.0017606339961074389 30 0.0 70 @@ -2463,9 +2463,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.07244466529351018 +-0.1377463748492844 20 --0.0121664667878012 +-0.002543803063728539 30 0.0 70 @@ -2485,9 +2485,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.07608206678934243 +-0.14726911169321127 20 --0.013664002507364326 +-0.0034269771921121408 30 0.0 70 @@ -2507,9 +2507,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.0797194682851746 +-0.15722362617736882 20 --0.015161538226927451 +-0.004390969826379197 30 0.0 70 @@ -2529,9 +2529,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.08335686978100681 +-0.16740814066690388 20 --0.016659073946490688 +-0.005416594411650697 30 0.0 70 @@ -2551,9 +2551,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.08697507316482567 +-0.17762087752696346 20 --0.01820113484491409 +-0.006484664393047595 30 0.0 70 @@ -2573,9 +2573,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.09056097837690974 +-0.18766005912269434 20 --0.019818103206756144 +-0.00757599321569088 30 0.0 70 @@ -2595,9 +2595,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.09414688358899384 +-0.19732390781924355 20 --0.02143507156859814 +-0.008671394324701528 30 0.0 70 @@ -2617,9 +2617,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.09773278880107797 +-0.20641064598175785 20 --0.023052039930440138 +-0.009751681165200496 30 0.0 70 @@ -2639,9 +2639,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.1013186940131621 +-0.21471849597538425 20 --0.02466900829228219 +-0.010797667182308755 30 0.0 70 @@ -2661,9 +2661,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.10490459922524614 +-0.22204568016526965 20 --0.026285976654124188 +-0.011790165821147313 30 0.0 70 @@ -2683,9 +2683,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.10849050443733027 +-0.2281904209165609 20 --0.027902945015966185 +-0.01270999052683711 30 0.0 70 @@ -2705,9 +2705,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.1120764096494144 +-0.23295094059440502 20 --0.029519913377808238 +-0.013537954744499132 30 0.0 70 @@ -2727,9 +2727,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.11566231486149847 +-0.23979996360677527 20 --0.031136881739650235 +-0.015129520021566228 30 0.0 70 @@ -2749,9 +2749,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.11924822007358254 +-0.25148260503740166 20 --0.03275385010149223 +-0.017606857022919985 30 0.0 70 @@ -2771,9 +2771,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.12283412528566659 +-0.2579546191528017 20 --0.03437081846333434 +-0.01863173176617797 30 0.0 70 @@ -2793,9 +2793,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.12642003049775075 +-0.262226860215378 20 --0.03598778682517634 +-0.019412214991891653 30 0.0 70 @@ -2815,9 +2815,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.1300059357098348 +-0.2669374459702987 20 --0.037604755187018335 +-0.020999484552667723 30 0.0 70 @@ -2837,9 +2837,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.1335918409219189 +-0.27060990263285706 20 --0.03922172354886039 +-0.02168528748379199 30 0.0 70 @@ -2859,9 +2859,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.13717774613400305 +-0.27605391151655334 20 --0.040838691910702385 +-0.02187701263418331 30 0.0 70 @@ -2881,9 +2881,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.14372856522610838 +-0.28290739393172737 20 --0.04327496838266798 +-0.021542503621769647 30 0.0 70 @@ -2903,9 +2903,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.14995755911305256 +-0.29080827118871905 20 --0.04558405899092027 +-0.02064960406447897 30 0.0 70 @@ -2925,9 +2925,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.15582399389417462 +-0.2993944645978683 20 --0.04775445959307795 +-0.019166157580239235 30 0.0 70 @@ -2947,9 +2947,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.16132787111901833 +-0.3083038954695148 20 --0.04978616888891935 +-0.017060007786978408 30 0.0 70 @@ -2969,9 +2969,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.16646919233712754 +-0.3171744851139986 20 --0.05167918557822265 +-0.014298998302624445 30 0.0 70 @@ -2991,9 +2991,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.17124795909804597 +-0.32564415484165954 20 --0.053433508360766435 +-0.01085097274510531 30 0.0 70 @@ -3013,9 +3013,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.1756641729513177 +-0.3333508259628373 20 --0.05504913593632882 +-0.006683774732348999 30 0.0 70 @@ -3035,9 +3035,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.17971783544648637 +-0.339932419787872 20 --0.05652606700468815 +-0.0017652478822834355 30 0.0 70 @@ -3057,9 +3057,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.18340894813309583 +-0.3454881558252771 20 --0.05786430026562289 +0.004704833462746747 30 0.0 70 @@ -3079,9 +3079,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.18673751256069004 +-0.3483272761239312 20 --0.05906383441891133 +0.011776135399291367 30 0.0 70 @@ -3101,9 +3101,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.18970353027881282 +-0.3484514218784382 20 --0.06012466816433182 +0.018861860410078025 30 0.0 70 @@ -3123,9 +3123,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.1923070028370079 +-0.3463546852158496 20 --0.06104680020166264 +0.025802732601637842 30 0.0 70 @@ -3145,9 +3145,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.19454793178481922 +-0.342531158263217 20 --0.0618302292306821 +0.03243947608050197 30 0.0 70 @@ -3167,9 +3167,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.19642631867179056 +-0.337474933147592 20 --0.06247495395116859 +0.038612814953201464 30 0.0 70 @@ -3189,9 +3189,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.1979421650474658 +-0.3316801019960261 20 --0.06298097306290046 +0.04416347332626759 30 0.0 70 @@ -3211,9 +3211,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.1990954724613888 +-0.32564075693557093 20 --0.063348285265656 +0.04893217530623134 30 0.0 70 @@ -3233,9 +3233,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.2000151253740754 +-0.3188458778589197 20 --0.06361126741164846 +0.05341221504874015 30 0.0 70 @@ -3255,9 +3255,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.20276874829217467 +-0.3108571163669807 20 --0.06435484737989106 +0.058297362776538256 30 0.0 70 @@ -3277,9 +3277,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.205246815645514 +-0.3044121089074911 20 --0.06479549956536129 +0.06182324367359262 30 0.0 70 @@ -3299,9 +3299,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.20836029970179235 +-0.2985118429121997 20 --0.06480866119589457 +0.06441062011468514 30 0.0 70 @@ -3321,9 +3321,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.21302017272870882 +-0.2921573058128556 20 --0.06426976949932639 +0.06648025447459761 30 0.0 70 @@ -3343,9 +3343,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.21934980512311386 +-0.2865282882030938 20 --0.06318850973792922 +0.0679285676863985 30 0.0 70 @@ -3365,9 +3365,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.2223415227029405 +-0.2832559809362735 20 --0.062085340964722235 +0.068596028695872 30 0.0 70 @@ -3387,9 +3387,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.22495973231109506 +-0.27901929595517305 20 --0.060292609672437714 +0.06934348373234978 30 0.0 70 @@ -3409,9 +3409,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.2273012549555563 +-0.2738182315326241 20 --0.057927912877090926 +0.07017093288219028 30 0.0 70 @@ -3431,9 +3431,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.22946291164430282 +-0.26765278594145847 20 --0.05510884759469742 +0.07107837623175192 30 0.0 70 @@ -3453,9 +3453,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.2315415233853133 +-0.26052295745450776 20 --0.05195301084127257 +0.07206581386739311 30 0.0 70 @@ -3475,9 +3475,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.2336339111865664 +-0.25242874434460383 20 --0.04857799963283188 +0.07313324587547225 30 0.0 70 @@ -3497,9 +3497,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.23583689605604086 +-0.2433701448845784 20 --0.04510141098539078 +0.07428067234234775 30 0.0 70 @@ -3519,9 +3519,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.23824729900171532 +-0.23334715734726305 20 --0.04164084191496459 +0.07550809335437807 30 0.0 70 @@ -3541,9 +3541,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.24096194103156843 +-0.22235978000548964 20 --0.038313889437569026 +0.07681550899792156 30 0.0 70 @@ -3563,9 +3563,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.24407764315357894 +-0.2104080111320898 20 --0.03523815056921925 +0.07820291935933668 30 0.0 70 @@ -3585,9 +3585,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.24769122637572547 +-0.19771046199064185 20 --0.032531222325930864 +0.0790226280364367 30 0.0 70 @@ -3607,9 +3607,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.25189527154487523 +-0.185841662792443 20 --0.03031250748991371 +0.07936991036791603 30 0.0 70 @@ -3629,9 +3629,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.2556710587823801 +-0.1750579172700254 20 --0.028897726461885542 +0.07960915554997243 30 0.0 70 @@ -3651,9 +3651,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.259176350329518 +-0.16535923085392507 20 --0.027895338568229233 +0.07974036358803642 30 0.0 70 @@ -3673,9 +3673,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.26261507934256245 +-0.15674560897467776 20 --0.027062875912520212 +0.07976353448753852 30 0.0 70 @@ -3695,9 +3695,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.2661911789777869 +-0.14921705706281949 20 --0.026157870598334243 +0.07967866825390929 30 0.0 70 @@ -3717,9 +3717,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.2701085823914646 +-0.142773580548886 20 --0.02493785472924681 +0.07948576489257927 30 0.0 70 @@ -3739,9 +3739,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.2742767322269781 +-0.13741518486341311 20 --0.023287611479191006 +0.07918482440897898 30 0.0 70 @@ -3761,9 +3761,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.2773537617614356 +-0.13314187543693684 20 --0.02171288824969564 +0.07877584680853897 30 0.0 70 @@ -3783,9 +3783,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.28041119952074683 +-0.12995365769999287 20 --0.019761313469147057 +0.07825883209668975 30 0.0 70 @@ -3805,9 +3805,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.283386801947182 +-0.1254877079691722 20 --0.01745533373208813 +0.07684097193913388 30 0.0 70 @@ -3827,9 +3827,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.28621832548301135 +-0.11873677349468834 20 --0.014817395633061614 +0.07384188739430268 30 0.0 70 @@ -3849,9 +3849,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.28884352657050505 +-0.11106532790527829 20 --0.011869945766610546 +0.06986682283410413 30 0.0 70 @@ -3871,9 +3871,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.29120016165193335 +-0.10309967970240826 20 --0.008635430727277682 +0.0653738215810658 30 0.0 70 @@ -3893,9 +3893,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.29322598716956644 +-0.09546613738754436 20 --0.005136297109605947 +0.06082092695771521 30 0.0 70 @@ -3915,9 +3915,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.2948587595656746 +-0.08879100946215296 20 --0.0013949915081380992 +0.05666618228657999 30 0.0 70 @@ -3937,9 +3937,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.296036235282528 +-0.08370060442770015 20 -0.0025660394825827715 +0.05336763089018767 30 0.0 70 @@ -3959,9 +3959,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.2966961707623968 +-0.08160927675386886 20 -0.006724349268014074 +0.05197399227539537 30 0.0 70 @@ -3981,9 +3981,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.29677131237060905 +-0.07927815509870763 20 -0.010823895979494569 +0.050450872590407314 30 0.0 70 @@ -4003,9 +4003,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.29606911246228473 +-0.07613436097711551 20 -0.014181500952964832 +0.04841720074061562 30 0.0 70 @@ -4025,9 +4025,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.2945903225789705 +-0.07217789696133127 20 -0.01733619705562711 +0.04587297621157261 30 0.0 70 @@ -4047,9 +4047,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.29243224340788126 +-0.06740876562359346 20 -0.020277968983556938 +0.0428181984888305 30 0.0 70 @@ -4069,9 +4069,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.28969217563623184 +-0.06182696953614077 20 -0.022996801432829728 +0.03925286705794162 30 0.0 70 @@ -4091,9 +4091,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.28646741995123726 +-0.055432511271211765 20 -0.025482679099520955 +0.03517698140445818 30 0.0 70 @@ -4113,9 +4113,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.2828552770401123 +-0.04822539340104519 20 -0.02772558667970615 +0.030590541013932528 30 0.0 70 @@ -4135,9 +4135,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.278953047590072 +-0.04020561849787964 20 -0.029715508869460727 +0.025493545371916906 30 0.0 70 @@ -4157,9 +4157,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.27485803228833117 +-0.03137318913395361 20 -0.03144243036486016 +0.019885993963963525 30 0.0 70 @@ -4179,9 +4179,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.27066753182210485 +-0.02172810788150599 20 -0.032896335861979986 +0.013767886275624787 30 0.0 70 @@ -4201,9 +4201,9 @@ AcDbVertex 100 AcDb2dVertex 10 --0.2664788468786078 +-0.01127037731277512 20 -0.034067210056895614 +0.007139221792452783 30 0.0 70 @@ -4223,1547 +4223,7 @@ AcDbVertex 100 AcDb2dVertex 10 --0.26238927814505497 - 20 -0.034945037645682575 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -93 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2584961263086613 - 20 -0.03551980332441629 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -94 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2548966920566418 - 20 -0.035781491789172226 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -95 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2516882760762112 - 20 -0.03572008773602592 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -96 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.24842500237321694 - 20 -0.03541667366752854 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -97 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.24477200340806995 - 20 -0.035075636354958084 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -98 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.24082262719541409 - 20 -0.03471373073340123 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -99 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.23664618716419447 - 20 -0.034341520250685376 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -9A -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.23231199674335595 - 20 -0.03396956835463805 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -9B -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.22788936936184365 - 20 -0.033608438493086756 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -9C -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.22344761844860245 - 20 -0.03326869411385891 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -9D -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.21905605743257744 - 20 -0.03296089866478208 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -9E -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.21478399974271362 - 20 -0.03269561559368367 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -9F -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2107007588079559 - 20 -0.03248340834839114 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -A0 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.20687564805724937 - 20 -0.03233484037673212 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -A1 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.2033779809195389 - 20 -0.032260475126533905 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -A2 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.20027707082376967 - 20 -0.03227087604562412 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -A3 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.19698654858711234 - 20 -0.03238592413156666 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -A4 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.19318285904709753 - 20 -0.0325402622832367 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -A5 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.18931970887041638 - 20 -0.03270409348936071 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -A6 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.18539709781429586 - 20 -0.032877414975028385 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -A7 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.18141502563596268 - 20 -0.033060223965329594 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -A8 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.17737349209264375 - 20 -0.03325251768535398 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -A9 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1726381225467503 - 20 -0.03338911516149823 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -AA -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1659840849214246 - 20 -0.033316250989470886 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -AB -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.15964888467818186 - 20 -0.03324643511774267 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -AC -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1536325212730734 - 20 -0.033179667593903006 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -AD -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.14793499416215058 - 20 -0.03311594846554122 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -AE -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.14255630280146458 - 20 -0.03305527778024664 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -AF -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.13749644664706695 - 20 -0.032997655585608576 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -B0 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.13275542515500882 - 20 -0.03294308192921647 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -B1 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.12833323778134154 - 20 -0.032891556858659576 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -B2 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.12422988398211648 - 20 -0.032843080421527227 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -B3 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.12044536321338492 - 20 -0.03279765266540885 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -B4 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.11697967493119824 - 20 -0.03275527363789377 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -B5 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1138328185916076 - 20 -0.032715943386571245 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -B6 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.11100479365066446 - 20 -0.03267966195903066 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -B7 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.10849559956442015 - 20 -0.03264642940286139 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -B8 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.1063052357889259 - 20 -0.03261624576565281 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -B9 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.10443370178023303 - 20 -0.03258911109499418 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -BA -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.10288099699439296 - 20 -0.03256502543847484 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -BB -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.10164712088745684 - 20 -0.03254398884368426 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -BC -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.09994337892822275 - 20 -0.03249160766593884 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -BD -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.09722703434661115 - 20 -0.03234615182366568 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -BE -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.093771592897088 - 20 -0.032118852135510856 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -BF -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.08970115316225025 - 20 -0.03182178565127325 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -C0 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.0851398137246947 - 20 -0.03146702942075169 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -C1 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.08021167316701835 - 20 -0.031066660493744958 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -C2 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.07504083007181808 - 20 -0.03063275592005199 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -C3 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.06975138302169073 - 20 -0.03017739274947162 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -C4 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.06446743059923335 - 20 -0.029712648031802624 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -C5 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.0593130713870427 - 20 -0.029250598816843887 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -C6 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.05441240396771574 - 20 -0.028803322154394295 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -C7 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.04988952692384935 - 20 -0.028382895094252625 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -C8 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.045868538838040485 - 20 -0.028001394686217707 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -C9 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.04247353829288597 - 20 -0.027670897980088482 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -CA -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.03982862387098274 - 20 -0.027403482025663728 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -CB -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.03805789415492772 - 20 -0.02721122387274233 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -CC -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.036264793274075446 - 20 -0.0269216897066466 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -CD -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.032322371975447184 - 20 -0.025941611713292345 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -CE -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.027830560327799014 - 20 -0.0246016350498387 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -CF -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.023812359863461197 - 20 -0.02324602396599168 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -D0 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.021294698693667236 - 20 -0.022218126153409845 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -D1 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.01872618634040374 - 20 -0.02064227908050753 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -D2 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.015093256261121235 - 20 -0.018146755728090103 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -D3 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.011096281688223297 - 20 -0.01523911360159308 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -D4 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.007435635854113387 - 20 -0.012426910206452146 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -D5 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.004811691991195022 - 20 -0.010217703048102877 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -D6 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.003023760328472236 - 20 -0.008011242975078714 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -D7 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 --0.0014008764041681387 - 20 -0.004592179313590128 - 30 -0.0 - 70 -0 - 0 -VERTEX - 5 -D8 -330 -17 -100 -AcDbEntity - 8 -0 -100 -AcDbVertex -100 -AcDb2dVertex - 10 -0.0 +1.0181425078340335e-16 20 0.0 30 From 9984c032600b60ec1017623890e47196107d4f68 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Fri, 19 Jun 2026 16:47:07 +0200 Subject: [PATCH 132/134] Use realistic stiffness and density --- examples/fsi/fin_2d.jl | 62 +++++++++++++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 16 deletions(-) diff --git a/examples/fsi/fin_2d.jl b/examples/fsi/fin_2d.jl index c8b4326b86..fc92dbfffd 100644 --- a/examples/fsi/fin_2d.jl +++ b/examples/fsi/fin_2d.jl @@ -17,20 +17,39 @@ tspan = (0.0, 2.0) fin_length = 0.502 fin_thickness = 30e-3 -real_thickness = 1e-3 real_modulus = 125e9 poisson_ratio = 0.3 -flexural_rigidity = real_modulus * real_thickness^3 / (1 - poisson_ratio^2) / 12 -modulus = 12 * (1 - poisson_ratio^2) * flexural_rigidity / fin_thickness^3 + +# Real blade thickness profile along the flexible blade: +# x = 0 is the attachment to the foot pocket, x = 1 is the blade tip. +function real_thickness(x) + real_thickness_at_attachment = 1.2e-3 + real_thickness_at_tip = 0.7e-3 + + # Clamp to use constant material properties for the clamped region and foot pocket. + x_clamped = clamp(x, 0.0, 1.0) + return real_thickness_at_attachment + + x_clamped * (real_thickness_at_tip - real_thickness_at_attachment) +end + +# The simulated blade is artificially thick for resolution. Scale Young's modulus so +# E_artificial * t_artificial^3 matches the real bending stiffness E_real * t_real^3. +function artificial_modulus(real_modulus, real_thickness, artificial_thickness) + return real_modulus * (real_thickness / artificial_thickness)^3 +end + +# Scale density so rho_artificial * t_artificial keeps the same mass per blade area +# as rho_real * t_real. +function artificial_density(real_density, real_thickness, artificial_thickness) + return real_density * real_thickness / artificial_thickness +end fiber_volume_fraction = 0.6 fiber_density = 1800.0 epoxy_density = 1250.0 real_density = fiber_volume_fraction * fiber_density + (1 - fiber_volume_fraction) * epoxy_density -# Scale the density with the thickness ratio to keep the mass of the fin constant -# when changing the thickness. -density = real_density * (fin_thickness / real_thickness) +density = real_density tank_size = (2.0, 1.0) center = (tank_size[2] / 2, tank_size[2] / 2) @@ -220,6 +239,26 @@ else fluid = setdiff(tank.fluid, structure) end +# Convert particle x-positions to the relative blade coordinate used by `real_thickness`. +# A value of 0 corresponds to the blade attachment, and a value of 1 corresponds to the tip. +function normalized_blade_coordinate(coordinates, particle) + return (coordinates[1, particle] - center[1]) / fin_length +end + +real_thickness_structure = [real_thickness(normalized_blade_coordinate(structure.coordinates, + particle)) + for particle in 1:nparticles(structure)] + +modulus = [artificial_modulus(real_modulus, thickness, fin_thickness) + for thickness in real_thickness_structure] + +# Update both density and mass based on the artificial thickness to ensure that +# the structure has the same mass per blade area as the real fin, +# while keeping the same bending stiffness. +structure.density .= [artificial_density(real_density, thickness, fin_thickness) + for thickness in real_thickness_structure] +structure.mass .= structure.density .* particle_spacing^2 + n_clamped_particles = nparticles(structure) - nparticles(beam) # Movement function @@ -256,12 +295,6 @@ boundary_model_structure = BoundaryModelDummyParticles(hydrodynamic_densites, smoothing_kernel, smoothing_length_fluid, viscosity=viscosity_fin) -# k_structure = 1.0 -# beta_structure = fluid_particle_spacing / particle_spacing -# boundary_model_structure = BoundaryModelMonaghanKajtar(k_structure, beta_structure, -# particle_spacing, -# hydrodynamic_masses) - viscosity_structure = ArtificialViscosityMonaghan(alpha=0.2) structure_system = TotalLagrangianSPHSystem(structure; smoothing_kernel, smoothing_length=smoothing_length_structure, young_modulus=modulus, poisson_ratio, @@ -285,9 +318,6 @@ fluid_system = WeaklyCompressibleSPHSystem(fluid; density_calculator=fluid_densi shifting_technique=ParticleShiftingTechnique(sound_speed_factor=0.2, v_max_factor=0.0), pressure_acceleration=tensile_instability_control, buffer_size=n_buffer_particles) -# fluid_system = EntropicallyDampedSPHSystem(fluid, smoothing_kernel, smoothing_length, -# sound_speed, viscosity=ViscosityAdami(; nu), -# transport_velocity=TransportVelocityAdami(10 * sound_speed^2 * fluid_density)) # ========================================================================================== # ==== Open Boundaries @@ -411,7 +441,7 @@ interpolate_cb = PostprocessCallback(; plane_vtk, dt=0.01, filename="plane") mechanical_work_calculator = MechanicalWorkCalculator(semi.systems[4], semi) thrust_calculator = ThrustCalculator(semi.systems[4], semi, direction=SVector(1.0, 0.0)) calculator_cb = PostprocessCallback(; mechanical_work_calculator, thrust_calculator, - interval=1, filename="$(prefix)_efficiency", + interval=100, filename="$(prefix)_efficiency", write_file_interval=1000) callbacks = CallbackSet(info_callback, saving_callback, From 6fbf220055b323741b7a2283f26dcd25f8370c55 Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Sun, 21 Jun 2026 01:25:14 +0200 Subject: [PATCH 133/134] Stabilize new fin model with more viscosity --- examples/fsi/fin_2d.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/fsi/fin_2d.jl b/examples/fsi/fin_2d.jl index fc92dbfffd..a7359700de 100644 --- a/examples/fsi/fin_2d.jl +++ b/examples/fsi/fin_2d.jl @@ -295,7 +295,7 @@ boundary_model_structure = BoundaryModelDummyParticles(hydrodynamic_densites, smoothing_kernel, smoothing_length_fluid, viscosity=viscosity_fin) -viscosity_structure = ArtificialViscosityMonaghan(alpha=0.2) +viscosity_structure = ArtificialViscosityMonaghan(alpha=1.0) structure_system = TotalLagrangianSPHSystem(structure; smoothing_kernel, smoothing_length=smoothing_length_structure, young_modulus=modulus, poisson_ratio, clamped_particles=1:n_clamped_particles, From 9fd00a5d0dbfe9438aad98a07b7d0850e57a34eb Mon Sep 17 00:00:00 2001 From: Erik Faulhaber <44124897+efaulhaber@users.noreply.github.com> Date: Tue, 23 Jun 2026 16:05:33 +0200 Subject: [PATCH 134/134] Fix `SerialBackend` --- src/general/neighborhood_search.jl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/general/neighborhood_search.jl b/src/general/neighborhood_search.jl index a2b6c22fdf..4d5fe89555 100644 --- a/src/general/neighborhood_search.jl +++ b/src/general/neighborhood_search.jl @@ -280,11 +280,16 @@ function initialize_neighborhood_search!(semi, system, neighbor) # TODO Initialize after adapting to the GPU. # Currently, this cannot use `semi.parallelization_backend` # because data is still on the CPU. + parallelization_backend = if semi.parallelization_backend isa KernelAbstractions.GPU + PolyesterBackend() + else + semi.parallelization_backend + end PointNeighbors.initialize!(get_neighborhood_search(system, neighbor, semi), initial_coordinates(system), initial_coordinates(neighbor), eachindex_y=each_active_particle(neighbor), - parallelization_backend=PolyesterBackend()) + parallelization_backend=parallelization_backend) return semi end