Skip to content

Commit

Permalink
Updating version to 4.3.0
Browse files Browse the repository at this point in the history
  • Loading branch information
hannorein committed Feb 11, 2024
1 parent 5f1d9a3 commit 4f7b990
Show file tree
Hide file tree
Showing 18 changed files with 472 additions and 38 deletions.
2 changes: 1 addition & 1 deletion README.md
@@ -1,4 +1,4 @@
[![Version](https://img.shields.io/badge/rebound-v4.2.0-green.svg?style=flat)](https://rebound.readthedocs.org)
[![Version](https://img.shields.io/badge/rebound-v4.3.0-green.svg?style=flat)](https://rebound.readthedocs.org)
[![PyPI](https://badge.fury.io/py/rebound.svg)](https://badge.fury.io/py/rebound)
[![GPL](https://img.shields.io/badge/license-GPL-green.svg?style=flat)](https://github.com/hannorein/rebound/blob/main/LICENSE)
[![Paper](https://img.shields.io/badge/arXiv-1110.4876-green.svg?style=flat)](https://arxiv.org/abs/1110.4876)
Expand Down
5 changes: 5 additions & 0 deletions changelog.md
Expand Up @@ -4,6 +4,11 @@ This changelog only includes the most important changes in recent updates. For a

## Version 4.x

### Version 4.3.0
* Take screenshots of WebGL based visualizations using the `reb_simulation_output_screenshot()` function. You need to connect one web browser to the simulation in order to take screenshots.
* Improved synchronization of visualization and simulation on Windows with mutex.
* Fixes an issue that might lead to NaN values when less than the maximum number of planets are used in WHFast512.

### Version 4.2.0
* It is now possible to programmatically change all aspects of a REBOUND visualization. This can be used to set up default viewing options or to render animations. See the C examples in `animation_solar_system` and `animation_saturn_rings`.
* Reworked matrix operations in visualization routines to follow the Model-View-Projection paradigm.
Expand Down
2 changes: 2 additions & 0 deletions docs/c_examples/generate_c_examples.py
Expand Up @@ -14,6 +14,8 @@ def run(*args, **kwargs):
# Manual exception for file viewer
if "_viewer" in cname:
livepreview=0
if "screenshots" in cname:
livepreview=0
try:
with open("examples/"+cname+"/Makefile","r") as mfd:
Makefile = mfd.read()
Expand Down
35 changes: 35 additions & 0 deletions examples/screenshots/Makefile
@@ -0,0 +1,35 @@
export OPENGL=0# Set this to 1 to enable OpenGL
export SERVER=1# Set this to 1 to enable the visualization web server
include ../../src/Makefile.defs

# CCPROBLEM is defined in Makefile.defs to allow for
# a compact cross platform Makefile
.PHONY: all librebound
all: problem.c librebound
@echo "Compiling $< ..."
$(CCPROBLEM)
@echo ""
@echo "Compilation successful. To run REBOUND, execute the file '$(EXEREBOUND)'."
@echo ""

librebound:
@echo "Compiling shared library $(LIBREBOUND) ..."
$(MAKE) -C ../../src/
@-$(RM) $(LIBREBOUND)
@$(LINKORCOPYLIBREBOUND)
@echo ""

clean:
@echo "Cleaning up shared library $(LIBREBOUND) ..."
$(MAKE) -C ../../src/ clean
@echo "Cleaning up local directory ..."
@-$(RM) $(LIBREBOUND)
@-$(RM) $(EXEREBOUND)

rebound_webgl.html: problem.c
@echo "Compiling problem.c with emscripten (WebGL enabled)..."
emcc -O3 -I../../src/ ../../src/*.c problem.c -DSERVERHIDEWARNING -DOPENGL=1 -sSTACK_SIZE=655360 -s USE_GLFW=3 -s FULL_ES3=1 -sASYNCIFY -sALLOW_MEMORY_GROWTH -sSINGLE_FILE -sEXPORTED_RUNTIME_METHODS="callMain" --shell-file ../../web_client/shell_rebound_webgl.html -o rebound_webgl.html

rebound_console.html: problem.c
@echo "Compiling problem.c with emscripten (WebGL disabled)..."
emcc -O3 -I../../src/ ../../src/*.c problem.c -DSERVERHIDEWARNING -sSTACK_SIZE=655360 -sASYNCIFY -sALLOW_MEMORY_GROWTH -sSINGLE_FILE -sEXPORTED_RUNTIME_METHODS="callMain" --shell-file ../../web_client/shell_rebound_console.html -o rebound_console.html
65 changes: 65 additions & 0 deletions examples/screenshots/problem.c
@@ -0,0 +1,65 @@
/**
* Screenshots
*
* This example shows how to take a screenshot of a REBOUND
* simulation at the beginning of the simulation and once
* every 400 timesteps during the integration. You need to
* compile REBOUND with SERVER=1 and connect a webbrowser
* to the simulation. Only then can you take screenshots.
* You might also be interested in the examples:
* 1) animation_saturns_rings and
* 2) animation_solar_system.
* They show how one can programatically change the
* visualization. You can combine this with taking
* screenshots if you want to record animations of REBOUND
* simulations.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "rebound.h"

void heartbeat(struct reb_simulation* const r){
if (r->steps_done%400==0){ // Every 400 timesteps
int id = r->steps_done/400;
char filename[1024];
sprintf(filename, "screenshot_%05d.png", id);
if (reb_simulation_output_screenshot(r, filename)){
printf("Screenshot saved: %s\n", filename);
}
}
}

int main(int argc, char* argv[]) {
struct reb_simulation* r = reb_simulation_create();
r->integrator = REB_INTEGRATOR_WHFAST;
r->heartbeat = heartbeat;

// Initial conditions
reb_simulation_add_fmt(r, "m", 1.); // star
reb_simulation_add_fmt(r, "m a", 1e-3, 1.); // planet 1
reb_simulation_add_fmt(r, "m a", 1e-3, 2.); // planet 2
reb_simulation_move_to_com(r);

// Start the web server. Make sure you point your
// webbrowser to http://localhost:1234
reb_simulation_start_server(r, 1234);

// Manually take a screenshot of the initial conditions.
// The program will pause here until you connect a
// webbrowser and a screenshot can be taken.
reb_simulation_output_screenshot(r, "screenshot_initial.png");
printf("Screenshot saved: screenshot_initial.png\n");

// Start integration.
reb_simulation_integrate(r, 10);

// Manually take a screenshot of the final simulation
reb_simulation_output_screenshot(r, "screenshot_final.png");
printf("Screenshot saved: screenshot_final.png\n");

// Cleanup
reb_simulation_free(r);
}

1 change: 1 addition & 0 deletions mkdocs.yml
Expand Up @@ -135,6 +135,7 @@ nav:
- c_examples/thermalhysteresis.md
- ipython_examples/SaturnsRings.ipynb
- "Visualization":
- c_examples/screenshots.md
- c_examples/animation_saturns_rings.md
- c_examples/animation_solar_system.md
- ipython_examples/OrbitPlot.ipynb
Expand Down
4 changes: 3 additions & 1 deletion rebound/simulation.py
Expand Up @@ -1439,7 +1439,9 @@ class timeval(Structure):
class ServerData(Structure):
_fields_ = [
("r", POINTER(Simulation)),
("r_copy", POINTER(Simulation)),
("_screenshot", c_void_p),
("_N_screenshot", c_size_t),
("_status_before_screenshot", c_int),
("port", c_int),
("need_copy", c_int),
("ready", c_int),
Expand Down
4 changes: 2 additions & 2 deletions setup.py
Expand Up @@ -18,7 +18,7 @@
ghash = subprocess.check_output(["git", "rev-parse", "HEAD"]).decode("ascii")
ghash_arg = "-DGITHASH="+ghash.strip()
except:
ghash_arg = "-DGITHASH=9aae43511b6ba3db0983edf762d26beabce37de7" #GITHASHAUTOUPDATE
ghash_arg = "-DGITHASH=5f1d9a3a5991af70aaf7012a3eb8b8f107350e09" #GITHASHAUTOUPDATE

extra_link_args=[]
if sys.platform == 'darwin':
Expand Down Expand Up @@ -83,7 +83,7 @@
long_description = f.read()

setup(name='rebound',
version='4.2.0',
version='4.3.0',
description='An open-source multi-purpose N-body code',
long_description=long_description,
long_description_content_type="text/markdown",
Expand Down
16 changes: 15 additions & 1 deletion src/display.c
Expand Up @@ -43,7 +43,6 @@
#include "integrator.h"
#define MAX(a, b) ((a) < (b) ? (b) : (a)) ///< Returns the maximum of a and b


static void reb_display_set_default_view(struct reb_simulation* const r, struct reb_display_settings* s){
float scale = 0.;
// Need a scale for visualization
Expand Down Expand Up @@ -174,6 +173,7 @@ EM_JS(int, reb_overlay_help_show, (int show), {
}
return show;
});

EM_JS(void, reb_overlay_hide, (int hide), {
var overlay = document.getElementById("overlay");
if (hide){
Expand Down Expand Up @@ -879,6 +879,10 @@ EM_BOOL reb_render_frame_emscripten(double time, void* p){
sprintf(line, "Simulation is running<br />");
}else if (data->r_copy->status == REB_STATUS_PAUSED){
sprintf(line, "Simulation is paused<br />");
}else if (data->r_copy->status == REB_STATUS_SCREENSHOT_READY){
sprintf(line, "Screenshot ready<br />");
}else if (data->r_copy->status == REB_STATUS_SCREENSHOT){
sprintf(line, "Taking screenshot<br />");
}else if (data->r_copy->status == REB_STATUS_SUCCESS){
sprintf(line, "Simulation ready<br />");
}else if (data->r_copy->status == REB_STATUS_USER){
Expand Down Expand Up @@ -920,6 +924,16 @@ EM_BOOL reb_render_frame_emscripten(double time, void* p){
reb_overlay_help_set_text(str);
}
}
if (data->r_copy->status == REB_STATUS_SCREENSHOT && !data->screenshot){
data->screenshot = EM_ASM_PTR({
var canvas = document.getElementById('canvas');
return stringToNewUTF8(canvas.toDataURL());
});
if (!data->screenshot){
printf("Error, screenshot not successful.");
}
data->r->status = REB_STATUS_SCREENSHOT_READY; // changing main simulation as r_copy will be overwritten
}
return EM_TRUE;
}
#endif
Expand Down
55 changes: 55 additions & 0 deletions src/output.c
Expand Up @@ -265,6 +265,61 @@ EM_JS(void, reb_remove_last_line, (), {
});
#endif

int reb_simulation_output_screenshot(struct reb_simulation* r, const char* filename){
#ifdef SERVER
if (!r->server_data){
reb_simulation_error(r, "To take a screenshot, call reb_simulation_start_server() and connect a web browser.");
return 0;
}

r->server_data->status_before_screenshot = r->status;
// Tell client to take screenshot
r->status = REB_STATUS_SCREENSHOT;

// Release mutex so client can pull simulation
if (r->server_data->mutex_locked_by_integrate){
#ifdef _WIN32
ReleaseMutex(r->server_data->mutex);
#else // _WIN32
pthread_mutex_unlock(&(r->server_data->mutex));
#endif // _WIN32
}

// Wait until screenshot arrives
while (!r->server_data->screenshot && r->status <0){
usleep(100);
if (reb_sigint== 1){
r->status = REB_STATUS_SIGINT;
}
}

// Lock mutex again before continuing
if (r->server_data->mutex_locked_by_integrate){
#ifdef _WIN32
WaitForSingleObject(r->server_data->mutex, INFINITE);
#else // _WIN32
pthread_mutex_lock(&(r->server_data->mutex));
#endif // _WIN32
}

r->status = r->server_data->status_before_screenshot;

if (r->server_data->screenshot){
FILE* f = fopen(filename,"wb");
fwrite(r->server_data->screenshot, r->server_data->N_screenshot, 1, f);
fclose(f);
free(r->server_data->screenshot);
r->server_data->screenshot = 0;
r->server_data->N_screenshot = 0;
return 1;
}
#else //SERVER
reb_simulation_error(r, "To take a screenshot compile with SERVER=1, call reb_simulation_start_server(), and connect with a web browser.");
#endif //SERVER
return 0;
}


void reb_simulation_output_timing(struct reb_simulation* r, const double tmax){
const int N = r->N;
#ifdef MPI
Expand Down
8 changes: 5 additions & 3 deletions src/rebound.c
Expand Up @@ -66,7 +66,7 @@ void usleep(__int64 usec);
const int reb_max_messages_length = 1024; // needs to be constant expression for array size
const int reb_N_max_messages = 10;
const char* reb_build_str = __DATE__ " " __TIME__; // Date and time build string.
const char* reb_version_str = "4.2.0"; // **VERSIONLINE** This line gets updated automatically. Do not edit manually.
const char* reb_version_str = "4.3.0"; // **VERSIONLINE** This line gets updated automatically. Do not edit manually.
const char* reb_githash_str = STRINGIFY(GITHASH); // This line gets updated automatically. Do not edit manually.

static int reb_simulation_error_message_waiting(struct reb_simulation* const r);
Expand Down Expand Up @@ -649,7 +649,7 @@ int reb_check_exit(struct reb_simulation* const r, const double tmax, double* la
r->status++;
}
}
while(r->status == REB_STATUS_PAUSED){
while(r->status == REB_STATUS_PAUSED || r->status == REB_STATUS_SCREENSHOT){
// Wait for user to disable paused simulation
#ifdef __EMSCRIPTEN__
emscripten_sleep(100);
Expand Down Expand Up @@ -802,7 +802,7 @@ static void* reb_simulation_integrate_raw(void* args){
if (r->testparticle_hidewarnings==0 && reb_particle_check_testparticles(r)){
reb_simulation_warning(r,"At least one test particle (type 0) has finite mass. This might lead to unexpected behaviour. Set testparticle_hidewarnings=1 to hide this warning.");
}
if (r->status != REB_STATUS_PAUSED){ // Allow simulation to be paused initially
if (r->status != REB_STATUS_PAUSED && r->status != REB_STATUS_SCREENSHOT){ // Allow simulation to be paused initially
r->status = REB_STATUS_RUNNING;
}
reb_run_heartbeat(r);
Expand Down Expand Up @@ -840,6 +840,7 @@ static void* reb_simulation_integrate_raw(void* args){
#else // _WIN32
pthread_mutex_lock(&(r->server_data->mutex));
#endif // _WIN32
r->server_data->mutex_locked_by_integrate = 1;
}
#endif //SERVER
if (r->simulationarchive_filename){ reb_simulationarchive_heartbeat(r);}
Expand All @@ -860,6 +861,7 @@ static void* reb_simulation_integrate_raw(void* args){
#else // _WIN32
pthread_mutex_unlock(&(r->server_data->mutex));
#endif // _WIN32
r->server_data->mutex_locked_by_integrate = 0;
}
#endif //SERVER
if (r->usleep > 0){
Expand Down
11 changes: 10 additions & 1 deletion src/rebound.h
Expand Up @@ -421,6 +421,8 @@ struct reb_integrator_janus {
enum REB_STATUS {
// Any status less than SINGLE_STEP get incremented once every timestep until SINGLE_STEP is reached.
REB_STATUS_SINGLE_STEP = -10, // Performing a single step, then switching to PAUSED.
REB_STATUS_SCREENSHOT_READY=-5,// Screenshot is ready, send back, then finish integration
REB_STATUS_SCREENSHOT = -4, // Pause until visualization has taken a screenshot.
REB_STATUS_PAUSED = -3, // Simulation is paused by visualization.
REB_STATUS_LAST_STEP = -2, // Current timestep is the last one. Needed to ensure that t=tmax exactly.
REB_STATUS_RUNNING = -1, // Simulation is current running, no error occurred.
Expand Down Expand Up @@ -688,6 +690,9 @@ DLLEXPORT void reb_simulation_output_ascii(struct reb_simulation* r, char* filen
DLLEXPORT void reb_simulation_output_velocity_dispersion(struct reb_simulation* r, char* filename);
// Function to allow for periodic outputs in heartbeat function. See examples on how to use it.
DLLEXPORT int reb_simulation_output_check(struct reb_simulation* r, double interval);
// Write a screenshot of the current simulation to a file. Requires that a server was started with reb_simulation_start_server() and one client web browser is connected.
// Returns 1 if successful, otherwise.
DLLEXPORT int reb_simulation_output_screenshot(struct reb_simulation* r, const char* filename);


// Timestepping
Expand Down Expand Up @@ -1090,11 +1095,14 @@ struct reb_vec4df {

struct reb_server_data {
struct reb_simulation* r;
struct reb_simulation* r_copy;
void* screenshot; // Screenshot data received by server (decoded)
size_t N_screenshot; // Size of decoded screenshot data
enum REB_STATUS status_before_screenshot;
int port;
int need_copy;
int ready;
#ifdef SERVER
int mutex_locked_by_integrate; // Let's heartbeat find out if it is being called while the mutex is locked.
#ifdef _WIN32
SOCKET socket;
HANDLE mutex; // Mutex to allow for copying
Expand Down Expand Up @@ -1124,6 +1132,7 @@ struct reb_display_data {
struct reb_display_settings s;
struct reb_simulation* r;
struct reb_simulation* r_copy;
void* screenshot; // Screenshot data to be sent to server
struct reb_vec4df* particle_data;
struct reb_orbit_opengl* orbit_data;
uint64_t N_allocated;
Expand Down

0 comments on commit 4f7b990

Please sign in to comment.