diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..650dee1 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,32 @@ +name: Run tests + +on: + push: + branches: + - master + - main + pull_request: + workflow_dispatch: + +jobs: + test: + runs-on: ${{ matrix.os }} + strategy: + matrix: + julia-version: ['1'] + julia-arch: [x64, x86] + os: [ubuntu-latest, windows-latest, macOS-latest] + exclude: + - os: macOS-latest + julia-arch: x86 + + steps: + - uses: actions/checkout@v2 + - uses: julia-actions/setup-julia@v1 + with: + version: ${{ matrix.julia-version }} + arch: ${{ matrix.julia-arch }} + - uses: julia-actions/julia-buildpkg@v1 + - uses: julia-actions/julia-runtest@v1 + # with: + # annotate: true diff --git a/Project.toml b/Project.toml index 43bb30f..ac8f6a3 100644 --- a/Project.toml +++ b/Project.toml @@ -5,8 +5,10 @@ version = "0.1.0" [deps] Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f" +DocumentFormat = "ffa9a821-9c82-50df-894e-fbcef3ed31cd" GeometryTypes = "4d00f742-c7ba-57c2-abde-4428a4b178cb" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" UnicodePlots = "b8865327-cd53-5732-bb35-84acbb429228" diff --git a/scripts/bench_init.jl b/scripts/bench_init.jl index 4b67f5d..16c5055 100644 --- a/scripts/bench_init.jl +++ b/scripts/bench_init.jl @@ -5,32 +5,29 @@ using Flase using BenchmarkTools println("gridsize, fraction, time, bytes") -for gridsize in (10, 20, 100, 200, 400, 800, #=1600, 2000=#) +for gridsize in (10, 20, 100, 200, 400, 800) #=1600, 2000=# for sheep_ration in (0.1, 0.5, 0.9) t = @timed begin - v0 = 5.0 - Dϕ = 4.0 - world = World( - v0 = 1., - n_dogs = 9, - boxsize = float(gridsize), - freedom_rate = 10.0, - business_rate = 10.0, - meanSleepyness = 10.0, - motion = BrownianMotion( - noise = v0^4 / Dϕ, - friction = Dϕ / v0^2 + v0 = 5.0 + Dϕ = 4.0 + world = World( + v0 = 1.0, + n_dogs = 9, + boxsize = float(gridsize), + freedom_rate = 10.0, + business_rate = 10.0, + meanSleepyness = 10.0, + motion = BrownianMotion(noise = v0^4 / Dϕ, friction = Dϕ / v0^2), + sheeps = Flase.SparseSheeps( + gridsize, # gridsize + n_sheeps = sheep_ration * gridsize^2, ), - sheeps = Flase.SparseSheeps( - gridsize, # gridsize - n_sheeps = sheep_ration * gridsize^2, - ) ) - simulation = FiniteSimulation(; - dt = 0.1, - end_time = 100.0, - world = world, - plotter = Flase.VoidPlotter() + simulation = FiniteSimulation(; + dt = 0.1, + end_time = 100.0, + world = world, + plotter = Flase.VoidPlotter(), ) end println("$gridsize, $sheep_ration, $(t.time), $(t.bytes)") diff --git a/scripts/bench_runsim.jl b/scripts/bench_runsim.jl index 96ba455..c49a149 100644 --- a/scripts/bench_runsim.jl +++ b/scripts/bench_runsim.jl @@ -5,34 +5,31 @@ using Flase using BenchmarkTools @time begin -v0 = 5.0 -Dϕ = 4.0 -gridsize = 200 -frac = 0.9 -world = World( - v0 = 1., - n_dogs = 9, - boxsize = float(gridsize), - freedom_rate = 10.0, - business_rate = 10.0, - meanSleepyness = 10.0, - motion = BrownianMotion( - noise = v0^4 / Dϕ, - friction = Dϕ / v0^2 + v0 = 5.0 + Dϕ = 4.0 + gridsize = 200 + frac = 0.9 + world = World( + v0 = 1.0, + n_dogs = 9, + boxsize = float(gridsize), + freedom_rate = 10.0, + business_rate = 10.0, + meanSleepyness = 10.0, + motion = BrownianMotion(noise = v0^4 / Dϕ, friction = Dϕ / v0^2), + sheeps = Flase.BaseSheeps( + gridsize, # gridsize + n_sheeps = frac * gridsize^2, ), - sheeps = Flase.BaseSheeps( - gridsize, # gridsize - n_sheeps = frac * gridsize^2, - ) ) -simulation = FiniteSimulation(; - dt = 0.1, - end_time = 100.0, - world = world, - plotter = Flase.VoidPlotter() + simulation = FiniteSimulation(; + dt = 0.1, + end_time = 100.0, + world = world, + plotter = Flase.VoidPlotter(), ) end t = @benchmark begin - Flase.runsim( simulation ) + Flase.runsim(simulation) end display(t) diff --git a/scripts/movingDogs.jl b/scripts/movingDogs.jl index 510d137..0c7b22a 100644 --- a/scripts/movingDogs.jl +++ b/scripts/movingDogs.jl @@ -1,17 +1,10 @@ using Flase world = World( - v0 = 1., + v0 = 1.0, n_dogs = 120, boxsize = 10.0, - motion = BrownianMotion( - noise = 0.5, - friction = 1.0 - ) - ) -simulation = InfiniteSimulation(; - dt = 0.05, - world = world, - plotter = UnicodePlotter() - ) -Flase.runsim( simulation ) + motion = BrownianMotion(noise = 0.5, friction = 1.0), +) +simulation = InfiniteSimulation(; dt = 0.05, world = world, plotter = UnicodePlotter()) +Flase.runsim(simulation) diff --git a/scripts/movingDogs_staticSheeps.jl b/scripts/movingDogs_staticSheeps.jl index 871bc2b..dc72543 100644 --- a/scripts/movingDogs_staticSheeps.jl +++ b/scripts/movingDogs_staticSheeps.jl @@ -5,33 +5,30 @@ using Flase using BenchmarkTools @time begin -v0 = 5.0 -Dϕ = 4.0 -gridsize = 10 -frac = 0.1 -world = World( - v0 = 1., - n_dogs = 9, - boxsize = float(gridsize), - freedom_rate = 10.0, - business_rate = 10.0, - meanSleepyness = 10.0, - motion = BrownianMotion( - noise = v0^4 / Dϕ, - friction = Dϕ / v0^2 + v0 = 5.0 + Dϕ = 4.0 + gridsize = 10 + frac = 0.1 + world = World( + v0 = 1.0, + n_dogs = 9, + boxsize = float(gridsize), + freedom_rate = 10.0, + business_rate = 10.0, + meanSleepyness = 10.0, + motion = BrownianMotion(noise = v0^4 / Dϕ, friction = Dϕ / v0^2), + sheeps = DenseSheeps( + gridsize, # gridsize + n_sheeps = frac * gridsize^2, ), - sheeps = DenseSheeps( - gridsize, # gridsize - n_sheeps = frac * gridsize^2, - ) ) -simulation = FiniteSimulation(; - dt = 0.1, - end_time = 100.0, - world = world, - plotter = Flase.VoidPlotter() + simulation = FiniteSimulation(; + dt = 0.1, + end_time = 100.0, + world = world, + plotter = Flase.VoidPlotter(), ) end @benchmark begin - Flase.runsim( simulation ) + Flase.runsim(simulation) end samples = 1 evals = 1 gcsample = true diff --git a/src/Flase.jl b/src/Flase.jl index cbef485..6cd5a0a 100644 --- a/src/Flase.jl +++ b/src/Flase.jl @@ -1,8 +1,8 @@ module Flase -export InfiniteSimulation, FiniteSimulation, runsim +export InfiniteSimulation, FiniteSimulation, runsim, ClusterTimeSimulation export BrownianMotion, ConstVelocity -export UnicodePlotter +export UnicodePlotter, VoidPlotter export World export DenseSheeps @@ -17,12 +17,12 @@ include("items/BaseSheeps.jl") include("items/DenseSheeps.jl") include("items/SparseSheeps.jl") +include("World.jl") + include("measures/Measure.jl") include("measures/MeanSquaredDisplacement.jl") include("measures/MeanQuadraticDistance.jl") -include("World.jl") - include("item-collector-interactions.jl") include("plotter/Plotter.jl") @@ -30,6 +30,7 @@ include("plotter/VoidPlotter.jl") include("plotter/UnicodePlotter.jl") include("simulations/Simulation.jl") +include("simulations/ClusterTimeSimulation.jl") include("simulations/InfiniteSimulation.jl") include("simulations/FiniteSimulation.jl") diff --git a/src/measures/MeanQuadraticDistance.jl b/src/measures/MeanQuadraticDistance.jl index dbbd49b..3d5d131 100644 --- a/src/measures/MeanQuadraticDistance.jl +++ b/src/measures/MeanQuadraticDistance.jl @@ -2,7 +2,16 @@ #first we need a new structure -struct MQD end +Base.@kwdef struct MQD + mqdNorm::Float64 +end + +MQD() = MQD(1) + +function MQD(sheeps::Sheeps) + mqdNorm = measure(MQD(), sheeps) + return MQD(mqdNorm) +end #then compute the function that takes imput from object sheep::Sheeps function sqr_dist(sheep, sheep1, gridsizestorage) @@ -19,13 +28,18 @@ function sqr_dist(sheep, sheep1, gridsizestorage) return d1 * d1 + d2 * d2 end - +function realSpace_sqr_dist(sheep, gridsizestorage, x::Real, y::Real, world::World) + ri = sheep[2][1] / gridsizestorage * world.boxsize[] + rj = sheep[2][2] / gridsizestorage * world.boxsize[] + d1 = min(abs(ri - x), world.boxsize[] - abs(ri - x)) + d2 = min(abs(rj - y), world.boxsize[] - abs(rj - y)) + return d1 * d1 + d2 * d2 +end function measure(::MQD, sheeps::Sheeps) #set a 3x3 Matrix for Test purpose. Result should be 3/4 #setvariables counter(=unsigned Integer), mean(=Float64) to 0 - gridsizestorage = size(sheeps.grid)[1] counter = 0 @@ -36,10 +50,7 @@ function measure(::MQD, sheeps::Sheeps) for sheep1 in sheeps - - - - mean += sheep[1] * sheep1[1] * SqrDist(sheep, sheep1, gridsizestorage) + mean += sheep[1] * sheep1[1] * sqr_dist(sheep, sheep1, gridsizestorage) counter += sheep[1] * sheep1[1] end @@ -48,5 +59,10 @@ function measure(::MQD, sheeps::Sheeps) return mean / counter +end + +function getMQD(mqd::MQD, sheeps::Sheeps) + mqd_value = measure(mqd, sheeps) / mqd.mqdNorm + return mqd_value end diff --git a/src/measures/MeanSquaredDisplacement.jl b/src/measures/MeanSquaredDisplacement.jl index fc6b0ae..82271e6 100644 --- a/src/measures/MeanSquaredDisplacement.jl +++ b/src/measures/MeanSquaredDisplacement.jl @@ -1,12 +1,28 @@ "Type for calculating the mean squared displacement." struct MSD end -function measure(::MSD, sheeps::Sheeps) +function measure(::MSD, sheeps::Sheeps, world::World) # get center of mass - cmx, cmy = center_of_mass(sheeps) + gridsizestorage = size(sheeps.grid)[1] + cmx, cmy = center_of_mass(sheeps, world) sum = 0 for sheep in sheeps - sum += real# sqr distance in real space + sum += realSpace_sqr_dist(sheep, gridsizestorage, cmx, cmy, world) # sqr distance in real space end return sum / length(sheeps) end + +function getClusterRadius(sheeps::Sheeps, world::World) + clusterRadius = + sqrt(sheeps.current_sheep[] / sheeps.capacity / π) * world.boxsize[] / + size(sheeps.grid)[1] + return clusterRadius +end + +function getMSD(msd::MSD, sheeps::Sheeps, world::World) + R_cl = getClusterRadius(sheeps, world) + R_item = size(sheeps.grid)[1] / 2 + msdNorm = (R_cl * R_cl) / 2 + R_cl * R_item + msd_value = msdNorm / measure(msd, sheeps, world) + return msd_value +end diff --git a/src/plotter/UnicodePlotter.jl b/src/plotter/UnicodePlotter.jl index 448b827..b0da6e0 100644 --- a/src/plotter/UnicodePlotter.jl +++ b/src/plotter/UnicodePlotter.jl @@ -44,7 +44,7 @@ end # function function plot!(io, p, plotter::UnicodePlotter, world::World, time) if plotter.counter[] % plotter.skip == zero(CountType) - for _ = 1:(UnicodePlots.nrows(p.graphics)+p.margin) + for _ = 1:(UnicodePlots.nrows(p.graphics)+p.margin.x) print(io, "\e[2K\e[1F") end # for p = plot(plotter, world, time) diff --git a/src/simulations/ClusterTimeSimulation.jl b/src/simulations/ClusterTimeSimulation.jl new file mode 100644 index 0000000..8423c07 --- /dev/null +++ b/src/simulations/ClusterTimeSimulation.jl @@ -0,0 +1,83 @@ +using Distributions + +struct ClusterTimeSimulation{P<:Plotter,F<:Number,W<:World} <: Simulation + + time::Base.RefValue{F} + t_sheep_boredom::Base.RefValue{F} + condition::Int64 + msdThreshold::Float64 + mqdThreshold::Float64 + dt::F + world::W + plotter::P + +end + +ClusterTimeSimulation(; + msdThreshold::Float64 = 0.7, + mqdThreshold::Float64 = 0.1, + dt::F, + world::W, + condition::Int64, + time::F = convert(typeof(dt), 0), + t_sheep_boredom = rand( + Exponential(world.meanSheepDiffusionTime / world.sheeps.current_sheep[]), + ), + plotter::P = UnicodePlotter(), +) where {F<:Number,W<:World,P<:Plotter} = ClusterTimeSimulation{P,F,W}( + Ref(time), + Ref(t_sheep_boredom), + condition, + msdThreshold, + mqdThreshold, + dt, + world, + plotter, +) + + +function runsim(sim::ClusterTimeSimulation) + msd = MSD() + mqd = MQD(sim.world.sheeps) + mqd_value = getMQD(mqd, sim.world.sheeps) + msd_value = getMSD(msd, sim.world.sheeps, sim.world) + + p = plot(sim.plotter, sim.world, sim.time[]) + io = IOBuffer() + if sim.condition == 0 + while msd_value < sim.msdThreshold + + measure(msd, sim.world.sheeps, sim.world) + iterate!(sim) + plot!(io, p, sim.plotter, sim.world, sim.time[]) + msd_value = getMSD(msd, sim.world.sheeps, sim.world) + + end + elseif sim.condition == 1 + while mqd_value > sim.mqdThreshold + + measure(mqd, sim.world.sheeps) + iterate!(sim) + plot!(io, p, sim.plotter, sim.world, sim.time[]) + mqd_value = getMQD(mqd, sim.world.sheeps) + + end + elseif sim.condition == 2 + while msd_value < sim.msdThreshold && mqd_value > sim.mqdThreshold + + measure(mqd, sim.world.sheeps) + iterate!(sim) + plot!(io, p, sim.plotter, sim.world, sim.time[]) + mqd_value = getMQD(mqd, sim.world.sheeps) + msd_value = getMSD(msd, sim.world.sheeps, sim.world) + + end + else + sim.condition == 3 + error("Check breaking condition! It is $condition right now.") + + end + + return sim.time[] + +end diff --git a/test/test_Simulation.jl b/test/test_Simulation.jl index f6677e4..aa68596 100644 --- a/test/test_Simulation.jl +++ b/test/test_Simulation.jl @@ -1,17 +1,18 @@ using Flase, Test world = World( - v0 = 1.0, - n_dogs = 120, - boxsize = 10.0, - motion = BrownianMotion(noise = 0.5, friction = 1.0), - sheeps = DenseSheeps(10, n_sheeps = 10), + v0=1.0, + n_dogs=120, + boxsize=10.0, + motion=BrownianMotion(noise=0.5, friction=1.0), + sheeps=DenseSheeps(10, n_sheeps=10), ) + simulation = FiniteSimulation(; - dt = 0.2, - end_time = 100.0, - world = world, - plotter = UnicodePlotter(), + dt=0.2, + end_time=100.0, + world=world, + plotter=UnicodePlotter() ) @testset "Move items" begin @@ -28,3 +29,35 @@ end # testset pos = simulation.world.dogs.member[3].position @test all(pos .< Flase.getSheepCoords(simulation.world, pos)) end + + +world2 = World( + v0=1.0, + n_dogs=10, + boxsize=25.0, + motion=BrownianMotion(noise=0.5, friction=1.0), + sheeps=DenseSheeps(30, n_sheeps=30), +) + +simulation2 = Flase.ClusterTimeSimulation(; + condition=0, + dt=0.2, + world=world2, + plotter=Flase.VoidPlotter() +) + +simulation3 = Flase.ClusterTimeSimulation(; + condition=1, + dt=0.2, + world=world, + plotter=Flase.VoidPlotter() +) + +@testset "ClusterTimeSimulation" begin + mqd = Flase.MQD(simulation2.world.sheeps) + msd = Flase.MSD() + @show Flase.runsim(simulation2) + @show Flase.runsim(simulation3) + @test Flase.getMSD(msd, simulation2.world.sheeps, simulation2.world) >= 0.7 + @test Flase.getMQD(mqd, simulation3.world.sheeps) <= 0.1 +end