diff --git a/Manifest.toml b/Manifest.toml index be1ca3fafc7e2a39a580d9000435a12e4a9136bc..4a51b1a671cd9e3bce5f2ddd7a1534935d09d2c4 100644 --- a/Manifest.toml +++ b/Manifest.toml @@ -1410,12 +1410,12 @@ uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" version = "2.7.2" [[deps.Persefone]] -deps = ["Agents", "ArgParse", "CSV", "CairoMakie", "DataFrames", "DataFramesMeta", "Dates", "Distributed", "FileIO", "GeoArrays", "ImageMagick", "Logging", "LoggingExtras", "Pkg", "Random", "Serialization", "StableRNGs", "StatsBase", "TOML", "Test"] -git-tree-sha1 = "b24521422c9b1a1099a8e9d1c4db06c4fdd7050a" -repo-rev = "development" +deps = ["Agents", "ArgParse", "CSV", "CairoMakie", "DataFrames", "DataFramesMeta", "Dates", "Distributed", "FileIO", "GeoArrays", "ImageMagick", "Logging", "LoggingExtras", "Pkg", "Random", "Serialization", "StableRNGs", "StatsBase", "TOML", "Test", "TiffImages"] +git-tree-sha1 = "389b532a6c7343ce8a8c5842a7949f98330b4d49" +repo-rev = "master" repo-url = "../model" uuid = "039acd1d-2a07-4b33-b082-83a1ff0fd136" -version = "0.2.0" +version = "0.3.2" [[deps.Pixman_jll]] deps = ["Artifacts", "CompilerSupportLibraries_jll", "JLLWrappers", "LLVMOpenMP_jll", "Libdl"] @@ -1487,11 +1487,11 @@ version = "1.9.0" [[deps.QML]] deps = ["ColorTypes", "CxxWrap", "Libdl", "MacroTools", "Observables", "Qt6Wayland_jll", "Requires", "jlqml_jll"] -git-tree-sha1 = "676fd7e228e5895c3a45e3d5fea3a619133c3af8" +git-tree-sha1 = "04a0b01b4bb6aff4f16be3854e019bd750dc5a3e" repo-rev = "main" repo-url = "https://github.com/JuliaGraphics/QML.jl.git" uuid = "2db162a6-7e43-52c3-8d84-290c1c42d82a" -version = "0.8.0" +version = "0.8.1" [[deps.QOI]] deps = ["ColorTypes", "FileIO", "FixedPointNumbers"] diff --git a/guiparams.toml b/guiparams.toml new file mode 100644 index 0000000000000000000000000000000000000000..04d95ec40fc90b423ad544c9adddbaa49310dfdd --- /dev/null +++ b/guiparams.toml @@ -0,0 +1,41 @@ +### Persefone.jl - a model of agricultural landscapes and ecosystems in Europe. +### +### This is the default configuration file for Persefone, containing all model parameters. +### The syntax is described here: https://toml.io/en/ +### +### DO NOT MODIFY THIS FILE FOR SIMULATION EXPERIMENTS! +### Instead, copy it to another directory and modify the copy. + +[core] +configfile = "guiparams.toml" # location of the configuration file +outdir = "desktop_results" # location and name of the output folder +overwrite = true # overwrite the output directory? (true/false/"ask") +csvoutput = false # save collected data in CSV files +visualise = false # generate result graphs +storedata = true # keep collected data in memory +loglevel = "debug" # verbosity level: "debug", "info", "warn" +processors = 2 # number of processors to use on parallel runs +seed = 2 # seed value for the RNG (0 -> random value) +# dates to start and end the simulation +startdate = 2022-01-01 +enddate = 2022-12-31 + +[world] +landcovermap = "data/landcover_jena.tif" # location of the landcover map +farmfieldsmap = "data/fields_jena.tif" # location of the field geometry map +weatherfile = "data/weather_jena.csv" # location of the weather data file + +[farm] +farmmodel = "FieldManager" # which version of the farm model to use (not yet implemented) + +[nature] +targetspecies = ["Wolpertinger", "Wyvern"] # list of target species to simulate +popoutfreq = "daily" # output frequency population-level data, daily/monthly/yearly/end/never +indoutfreq = "daily" # output frequency individual-level data, daily/monthly/yearly/end/never +insectmodel = ["season", "habitat", "pesticides", "weather"] # factors affecting insect growth + +[crop] +cropmodel = "almass" # crop growth model to use, "almass" or "aquacrop" +cropfile = "data/crop_data_general.csv" # file with general crop parameters +growthfile = "data/almass_crop_growth_curves.csv" # file with crop growth parameters + diff --git a/run.jl b/run.jl index 09d304a16fdf4cd1549b5a974538959de3526a5c..fd96f76b97c043b6bad596932cc2c31370cebf8f 100755 --- a/run.jl +++ b/run.jl @@ -7,7 +7,4 @@ using Pkg Pkg.activate(".") using PersefoneDesktop -# MUST disable threading in Qt -ENV["QSG_RENDER_LOOP"] = "basic" - launch() diff --git a/src/GUI.jl b/src/GUI.jl index d0f4c6e790aa5940ea301ab89d6fcf671bc04b95..271edf47fe5580638fabdb83f57959fdce4adafd 100644 --- a/src/GUI.jl +++ b/src/GUI.jl @@ -8,13 +8,15 @@ ## - https://github.com/barche/QmlJuliaExamples ## - https://doc.qt.io/qt-6/qtquick-index.html -#TODO define standard config file for Persefone Desktop +#FIXME we either have a memory leak, and/or too much memory consumed +# -> leads to SIGKILL after 3 months global model = nothing global landcovermap = nothing global runsimulation = nothing global timer = nothing +const configfile = Observable("guiparams.toml") const running = Observable(false) const ticks = Observable(0) const date = Observable(today()) @@ -27,7 +29,7 @@ function newsimulation() global model, landcovermap running[] = false progress[] = 0.0 - model = initialise() #TODO configure + model = initialise(configfile[]) landcovermap = load(@param(world.landcovermap)) date[] = model.date println("Model initialised.") @@ -72,11 +74,19 @@ function previousstep() println("Backtracked model: $(date[]-Day(1))") end +#XXX This could also be done with Channels rather than ResumableFunctions, +# which would save us one dependency (Channels are inbuilt). However, the +# ResumableFunctions version seemed to work more smoothly. @resumable function createrunfunction() + i = round(delay[]*10) while running[] - nextstep() - #XXX to reduce interface lag, always only sleep for 0.1s? - sleep(delay[]) + i -= 1 + if i <= 0 + nextstep() + i = round(delay[]*10) + end + # to reduce interface lag, always only sleep for 0.1s + sleep(0.1) @yield 1 end end @@ -98,13 +108,15 @@ function togglerunning() end function render_map(screen) + #FIXME individuals not plotted global model, landcovermap println("Updating map") - figure = visualisemap(model, date[], landcovermap) + figure = visualisemap(model, date[]-Day(1), landcovermap) display(screen, figure.scene) end function render_plot(screen) + #FIXME causes segfault when called during a simulation global model println("Updating plot") figure = populationtrends(model) @@ -124,6 +136,8 @@ on(ticks) do t running[] && runsimulation() end +datestring = () -> Dates.format(date[], "dd U yyyy") + function splashscreen() qmlfile = joinpath(dirname(@__FILE__), "splash.qml") quick_view = init_qquickview() @@ -131,7 +145,7 @@ function splashscreen() QML.show(quick_view) #XXX exec_async() is currently still broken, but should be fixed soon # (https://github.com/JuliaGraphics/QML.jl/issues/174) - @async exec() + @async exec() #FIXME do this with the timer? end """ @@ -142,15 +156,19 @@ The main function that creates the application. function launch() global model, timer #splashscreen() - newsimulation() + isnothing(model) && newsimulation() @qmlfunction newsimulation @qmlfunction nextstep @qmlfunction previousstep @qmlfunction togglerunning + @qmlfunction datestring timer = QTimer() + ENV["QSG_RENDER_LOOP"] = "basic" + QML.setGraphicsApi(QML.OpenGL) + qmlfile = joinpath(dirname(@__FILE__), "main.qml") loadqml(qmlfile, timer = timer, diff --git a/src/PersefoneDesktop.jl b/src/PersefoneDesktop.jl index 4d9372911c2e14a3483db4cb85fd8566a41fa7d9..72ab48235775c2a972905de24b4ee972448409fd 100644 --- a/src/PersefoneDesktop.jl +++ b/src/PersefoneDesktop.jl @@ -25,11 +25,11 @@ using Observables, ResumableFunctions -# needed for Makie integration -ENV["QSG_RENDER_LOOP"] = "basic" - include("GUI.jl") +precompile(launch, ()) +#precompile(render_map, (Screen)) #what's the input type? + export launch diff --git a/src/config.qml b/src/config.qml deleted file mode 100644 index 0a1e50c2d0e109b9321a16e5b459233c032489e3..0000000000000000000000000000000000000000 --- a/src/config.qml +++ /dev/null @@ -1,9 +0,0 @@ -/// PersefoneDesktop - a GUI to the Persefone model of agriculture and ecosystems -/// -/// This file defines the layout of the configuration window. -/// - -import QtQuick -import QtQuick.Controls -import QtQuick.Layouts -import org.julialang diff --git a/src/main.qml b/src/main.qml index a95a4ee1c1284c394c0fe86876dc87fdef0160a6..3e6f7e5f8c85e0cfe1eaa27bcfb82813ee5a36b3 100644 --- a/src/main.qml +++ b/src/main.qml @@ -30,13 +30,21 @@ ApplicationWindow { } Action { text: "&Load Saved State" } Action { text: "&Save Current State" } - Action { text: "Save Model &Output" } MenuSeparator { } Action { text: "&Quit" onTriggered: { mainWindow.close() } } } + Menu { + title: "&Data" + Action { + text: "Show &Population Graph" + onTriggered: { populationGraph.visible = true } + } + Action { text: "Save &Graphical Output" } + Action { text: "Save CSV Output" } + } Menu { title: "&Help" Action { @@ -53,69 +61,12 @@ ApplicationWindow { } } } - - MessageDialog { - id: aboutDialog - text: "Persefone.jl Desktop" - informativeText: "A mechanistic model of agricultural landscapes \ -and ecosystems in Europe.\n\n\ -© 2023 Daniel Vedder, Lea Kolb, Guy Pe'er\n\ -Distributed under the MIT license." - } - RowLayout { - spacing: 6 + // visualise the model map and the locations of animals + MakieViewport { + id: mapviewport anchors.fill: parent - - // visualise the model map and the locations of animals - MakieViewport { - id: mapviewport - Layout.fillWidth: true - Layout.fillHeight: true - renderFunction: render_map_callback - } - - ColumnLayout { - - // the calendar is no longer a single widget, but needs to be - // constructed manually :-( - ColumnLayout { - id: calenderWidget - Layout.alignment: Qt.AlignHCenter - Text { - text: "<b>"+Qt.formatDate(new Date(), "MMM yyyy")+"</b>" //FIXME - } - DayOfWeekRow { - locale: grid.locale - Layout.column: 1 - Layout.fillWidth: true - } - MonthGrid { - id: grid - //month: Calendar.December //FIXME - //year: 2015 //FIXME - locale: Qt.locale("en_GB") - Layout.fillWidth: true - Layout.fillHeight: true - delegate: Text { - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - opacity: model.month === grid.month ? 1 : 0 - text: model.today ? "<b>"+model.day+"</b>" : model.day - font: grid.font - required property var model - } - } - } - - // plot the population sizes over time - MakieViewport { - id: plotviewport - Layout.fillWidth: true - Layout.fillHeight: true - renderFunction: render_plot_callback //TODO - } - } + renderFunction: render_map_callback } // the main control bar, with pause/step/run buttons, the progress @@ -169,6 +120,34 @@ Distributed under the MIT license." ToolTip.visible: hovered onValueChanged: vars.delay = value } + Text { + id: dateText + text: Julia.datestring() + } + } + } + + // extra windows + MessageDialog { + id: aboutDialog + text: "Persefone.jl Desktop" + informativeText: "A mechanistic model of agricultural landscapes \ +and ecosystems in Europe.\n\n\ +© 2023 Daniel Vedder, Lea Kolb, Guy Pe'er\n\ +Distributed under the MIT license." + } + + Window { + id: populationGraph + title: "Population Graph" + width: 512 + height: 512 + visible: false + + MakieViewport { + id: plotviewport + anchors.fill: parent + renderFunction: render_plot_callback } } @@ -181,8 +160,9 @@ Distributed under the MIT license." JuliaSignals { signal updateMakie() onUpdateMakie: { + dateText.text = Julia.datestring(); mapviewport.update(); - plotviewport.update(); + //plotviewport.update(); } } diff --git a/src/splash.qml b/src/splash.qml index b731adc71d4b26c1a9377f511d62248328513d19..f7f3ca1d05061802791e02c767e5441836e835ad 100644 --- a/src/splash.qml +++ b/src/splash.qml @@ -8,12 +8,12 @@ import QtQuick.Controls import org.julialang Rectangle { - width: 1200 - height: 500 + width: 600 + height: 250 Image { source: "persefonejl_logo_v3_splash.png" - fillMode: Image.PreserveAspectFit + fillMode: Image.Stretch } Timer { // Show the splash screen while the model object is initialised