diff --git a/.github/workflows/test-pr.yml b/.github/workflows/test-pr.yml index 336f6eb..2664cc5 100644 --- a/.github/workflows/test-pr.yml +++ b/.github/workflows/test-pr.yml @@ -16,21 +16,16 @@ jobs: # Checkout the repository - name: Checkout code uses: actions/checkout@v4 + with: + fetch-depth: '0' + fetch-tags: 'true' - # Install Pixi - - name: Install pixi + # Install Pixi and workspace + - name: Install pixi and workspace uses: prefix-dev/setup-pixi@v0.8.0 with: cache: false - # Verify Pixi installation - - name: Verify Pixi version - run: pixi --version - - # Run pixi install - - name: Install dependencies - run: pixi install --locked - # Run setup - name: Run setup run: pixi run setup @@ -44,6 +39,6 @@ jobs: - name: Run example and verify output files run: | cd examples - python -m brian2wasm brunel_hakim1999.py --no-server + python -m brian2wasm brunel_hakim1999.py --no-server --skip-install ls brunel_hakim1999/{brian.js,index.html,wasm_module.{js,wasm},worker.js} shell: pixi run bash -e {0} \ No newline at end of file diff --git a/.gitignore b/.gitignore index c664027..a3549e4 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,6 @@ examples/output/ *.egg-info pixi.lock brian2wasm/_version.py -/dist \ No newline at end of file +/dist + +/docs/_build \ No newline at end of file diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000..b1cadbf --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,20 @@ +# .readthedocs.yaml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Set the OS, Python version and other tools you might need +build: + os: 'ubuntu-22.04' + tools: + python: "3.12" + +# Build documentation in the "docs/" directory with Sphinx +sphinx: + configuration: docs/conf.py + +python: + install: + - requirements: rtd-requirements.txt diff --git a/brian2wasm/__main__.py b/brian2wasm/__main__.py index ad2b522..4bcdc7d 100644 --- a/brian2wasm/__main__.py +++ b/brian2wasm/__main__.py @@ -7,50 +7,45 @@ def main(): """ - Command-line interface for **Brian2Wasm**. - - Usage - ----- - ``python -m brian2wasm [--no-server] [--skip-install]`` - - Parameters - ---------- - script : str - Path to the user’s Python model. The file **must** end with - ``.py`` and must not call ``set_device`` itself – the CLI inserts - the appropriate ``set_device('wasm_standalone', …)`` line - automatically. - --no-server : flag, optional - Generate the WASM/HTML output without starting the local preview - web-server (sets the ``BRIAN2WASM_NO_SERVER`` environment - variable for the subprocess). - --skip-install : flag, optional - Run Brian2WASM without checking or installing EMSDK. Use this if - you are sure EMSDK is already installed and configured in your - environment. - - Behaviour - --------- - 1. Validates that *script* exists and is a ``.py`` file. - 2. Looks for an ``.html`` file in the same directory. - * If found, passes the HTML file to ``set_device`` so the custom - template is used. - * Otherwise falls back to the default template. - 3. Unless *--skip-install* is given, verifies EMSDK installation - (Pixi/Conda/CONDA_EMSDK_DIR) and attempts to activate it. - 4. Prepends the required ``set_device('wasm_standalone', …)`` call to - the script source in-memory. - 5. Executes the modified script with its own directory as working - directory, so any relative paths inside the model behave as - expected. - - Exit status - ----------- - * ``0`` – build finished successfully (and server started unless - *--no-server* was given). - * ``1`` – any error (missing file, not a ``.py`` file, EMSDK not found - or not activated, exception during model execution, etc.). - """ + Command-line interface entry point for Brian2Wasm. + + This function validates the given script, injects the required + ``set_device('wasm_standalone', …)`` call, ensures EMSDK is + available (unless skipped), and executes the modified script. + + Parameters + ---------- + script : str + Path to the Python model file. The file must exist, end with + ``.py``, and must not call ``set_device`` directly, since this + function injects the correct call automatically. + --no-server : bool, optional + If given, generates the WASM/HTML output without starting the + local preview web server. Internally sets the environment + variable ``BRIAN2WASM_NO_SERVER=1``. + --skip-install : bool, optional + If given, skips EMSDK installation and activation checks. + Use this flag when you are certain EMSDK is already installed + and properly configured in your environment. + + Raises + ------ + FileNotFoundError + If the provided script path does not exist. + ValueError + If the provided file is not a Python ``.py`` script. + RuntimeError + If execution of the modified script fails for any reason. + SystemExit + If errors occur during validation or script execution, the + process exits with status code ``1``. + + Returns + ------- + None + This function is intended as a CLI entry point and does not + return a value. + """ parser = argparse.ArgumentParser( description="Brian2WASM CLI" @@ -76,10 +71,10 @@ def main(): # Check if the script exists and is a Python file if not os.path.isfile(script_path): full_path = os.path.abspath(script_path) - print(f"❌ Error: File '{full_path}' does not exist.", file=sys.stderr) + print(f"Error: File '{full_path}' does not exist.", file=sys.stderr) sys.exit(1) if not script_path.endswith(".py"): - print(f"❌ Error: File '{script_path}' is not a Python script (.py).", file=sys.stderr) + print(f"Error: File '{script_path}' is not a Python script (.py).", file=sys.stderr) sys.exit(1) if not args.skip_install: @@ -101,14 +96,14 @@ def main(): # Inject required lines at the top if has_html_file: - print(f"✅ HTML file found: '{html_file_path}'") + print(f"HTML file found: '{html_file_path}'") injection = ( "from brian2 import set_device\n" "import brian2wasm\n" f"set_device('wasm_standalone', directory='{script_name}', html_file='{html_file}')\n" ) else: - print("ℹ️ HTML file not found: using default HTML template.") + print("HTML file not found: using default HTML template.") injection = ( "from brian2 import set_device\n" "import brian2wasm\n" @@ -125,14 +120,14 @@ def main(): if args.no_server: os.environ['BRIAN2WASM_NO_SERVER'] = '1' - print(f"📄 Script path: {os.path.abspath(script_path)}") - print(f"📁 Directory: {script_dir}") + print(f"Script path: {os.path.abspath(script_path)}") + print(f"Directory: {script_dir}") exec_globals = {'__name__': '__main__', '__file__': os.path.abspath(script_path)} compiled_script = compile(modified_script, script_path, 'exec') exec(compiled_script, exec_globals) except Exception as e: - print(f"❌ Error running script: {e}", file=sys.stderr) + print(f"Error running script: {e}", file=sys.stderr) sys.exit(1) finally: @@ -140,11 +135,40 @@ def main(): def check_emsdk(): + """ + Verify that the Emscripten SDK (EMSDK) is installed and attempt to activate it. + + This function checks for EMSDK in the current environment, using either + the system path (``emsdk`` executable) or the ``CONDA_EMSDK_DIR`` variable. + If EMSDK is missing, it prints installation instructions and exits. + If EMSDK is found but not activated, it attempts to activate the latest + version, optionally prompting the user to install and activate it. + + Parameters + ---------- + None + This function takes no arguments. + + Raises + ------ + SystemExit + If EMSDK is not found, not activated, or installation/activation + fails, the process exits with status code ``1``. + RuntimeError + If subprocess execution encounters an unexpected failure during + EMSDK activation. + + Returns + ------- + None + This function is intended as a setup check and does not + return a value. Its success or failure is indicated by process exit. + """ emsdk = shutil.which("emsdk") conda_emsdk_dir = os.environ.get("CONDA_EMSDK_DIR") if not emsdk and not conda_emsdk_dir: - print("❌ EMSDK and CONDA_EMSDK_DIR not found. That means EMSDK is not installed.") + print("EMSDK and CONDA_EMSDK_DIR not found. That means EMSDK is not installed.") print(" ➤ If you are using **Pixi**, run:") print(" pixi add emsdk && pixi install") print(" ➤ If you are using **Conda**, run:") @@ -153,20 +177,20 @@ def check_emsdk(): print(" https://emscripten.org/index.html#") sys.exit(1) - print(f"✅ EMSDK is installed and CONDA_EMSDK_DIR is found") + print(f"EMSDK is installed and CONDA_EMSDK_DIR is found") try: - print("🔧 Attempting to activate EMSDK with: emsdk activate latest") + print("Attempting to activate EMSDK with: emsdk activate latest") result = subprocess.run(["./emsdk", "activate", "latest"], cwd=conda_emsdk_dir, check=False, capture_output=True, text=True) if result.returncode != 0: - print("❌ Failed to activate EMSDK:") + print("Failed to activate EMSDK:") choice = input("Do you want to install and activate EMSDK now? (y/n) ") if choice == 'y': try: subprocess.run(["./emsdk", "install", "latest"], cwd=conda_emsdk_dir, check=True) - print("✅ EMSDK install & activation succeeded. You can run the script now.") + print("EMSDK install & activation succeeded. You can run the script now.") except subprocess.CalledProcessError as e: - print("❌ Failed to activate EMSDK:") + print("Failed to activate EMSDK:") print(" ➤ Please run the following manually in your terminal and try again:") print(" cd $CONDA_EMSDK_DIR && ./emsdk install latest && ./emsdk activate latest") else: @@ -175,9 +199,9 @@ def check_emsdk(): sys.exit(1) else: - print("✅ EMSDK activation succeeded.") + print("EMSDK activation succeeded.") except Exception as e: - print(f"❌ Error while running EMSDK activation: {e}") + print(f"Error while running EMSDK activation: {e}") sys.exit(1) diff --git a/brian2wasm/device.py b/brian2wasm/device.py index ab1168e..f43efff 100644 --- a/brian2wasm/device.py +++ b/brian2wasm/device.py @@ -21,7 +21,6 @@ from brian2.devices.cpp_standalone.device import CPPStandaloneDevice, CPPWriter from brian2.utils.filetools import ensure_directory -__all__ = [] logger = get_logger(__name__) @@ -73,16 +72,81 @@ class WASMStandaloneDevice(CPPStandaloneDevice): The `Device` used for WASM simulations. """ def __init__(self, *args, **kwds): + """ + Initialize the WASM standalone device. + + This method prepares the device by setting up internal attributes + and delegating initialization to the parent ``CPPStandaloneDevice``. + + Parameters + ---------- + *args : tuple + Positional arguments passed to the parent ``CPPStandaloneDevice``. + **kwds : dict + Keyword arguments passed to the parent ``CPPStandaloneDevice``. + + Raises + ------ + None + + Returns + ------- + None + Initializes internal state; does not return a value. + """ self.transfer_results = None super(WASMStandaloneDevice, self).__init__(*args, **kwds) def transfer_only(self, variableviews): + """ + Mark variables for transfer from WASM to JavaScript. + + This method specifies which simulation variables should be available + in JavaScript after the simulation completes. + + Parameters + ---------- + variableviews : list + List of ``VariableView`` objects to be transferred. + + Raises + ------ + AssertionError + If transfer variables are already set before calling this method. + + Returns + ------- + None + Stores the selected variables for later transfer; does not return a value. + """ assert self.transfer_results is None self.transfer_results = [] for variableview in variableviews: self.transfer_results.append(variableview.variable) def activate(self, *args, **kwargs): + """ + Activate the WASM standalone device for simulation. + + This method overrides template configuration and ensures WASM-specific + headers are included in the generated code. + + Parameters + ---------- + *args : tuple + Positional arguments passed to the parent activate method. + **kwargs : dict + Keyword arguments passed to the parent activate method. + + Raises + ------ + None + + Returns + ------- + None + Configures the device and modifies build templates; does not return a value. + """ super(WASMStandaloneDevice, self).activate(*args, **kwargs) # Overwrite the templater to prefer our templates self.code_object_class().templater = self.code_object_class().templater.derive('brian2wasm') @@ -98,6 +162,37 @@ def generate_objects_source( networks, timed_arrays, ): + """ + Generate the main C++ source file for WASM compilation. + + This method produces the core simulation code, including objects, + arrays, and transfer variables, and writes it to ``objects.*`` files. + + Parameters + ---------- + writer : CodeWriter + Object for writing generated code. + arange_arrays : dict + Specifications for arange arrays. + synapses : set + Set of ``Synapses`` objects in the simulation. + static_array_specs : dict + Specifications for static arrays. + networks : set + Set of ``Network`` objects in the simulation. + timed_arrays : dict + Specifications for timed arrays. + + Raises + ------ + IOError + If writing the generated code to files fails. + + Returns + ------- + None + Generates source files on disk; does not return a value. + """ arr_tmp = self.code_object_class().templater.objects( None, None, @@ -120,6 +215,37 @@ def generate_objects_source( writer.write("objects.*", arr_tmp) def generate_makefile(self, writer, compiler, compiler_flags, linker_flags, nb_threads, debug): + """ + Generate a platform-specific makefile for Emscripten compilation. + + This method configures compiler and linker flags, resolves SDK paths, + and writes a makefile tailored for WASM builds. + + Parameters + ---------- + writer : CodeWriter + Object for writing generated files. + compiler : str + Compiler name (typically ``emcc``). + compiler_flags : str + Compiler flags to apply. + linker_flags : str + Linker flags to apply. + nb_threads : int + Number of threads (unused for WASM). + debug : bool + Whether to include debug symbols. + + Raises + ------ + RuntimeError + If Emscripten paths or build options are invalid. + + Returns + ------- + None + Writes the makefile to disk; does not return a value. + """ preloads = ' '.join(f'--preload-file static_arrays/{static_array}' for static_array in sorted(self.static_arrays.keys())) rm_cmd = 'rm $(OBJS) $(PROGRAM) $(DEPS)' @@ -175,6 +301,29 @@ def generate_makefile(self, writer, compiler, compiler_flags, linker_flags, nb_t writer.write(outputfile_name, makefile_tmp) def copy_source_files(self, writer, directory): + """ + Copy JavaScript runtime files to the build directory. + + This method copies required JavaScript files (``worker.js``, ``brian.js``) + and optionally a custom ``index.html`` into the build folder. + + Parameters + ---------- + writer : CodeWriter + Object containing source file information. + directory : str + Target directory for copied files. + + Raises + ------ + IOError + If copying files fails. + + Returns + ------- + None + Populates the build directory with JavaScript runtime files. + """ super(WASMStandaloneDevice, self).copy_source_files(writer, directory) shutil.copy(os.path.join(os.path.dirname(__file__), 'templates', 'worker.js'), directory) shutil.copy(os.path.join(os.path.dirname(__file__), 'templates', 'brian.js'), directory) @@ -182,6 +331,28 @@ def copy_source_files(self, writer, directory): shutil.copy(self.build_options['html_file'], os.path.join(directory, 'index.html')) def get_report_func(self, report): + """ + Generate C++ code for simulation progress reporting. + + This method produces source code that reports simulation progress + to the console or forwards updates to JavaScript via ``EM_ASM``. + + Parameters + ---------- + report : str or None + Type of progress reporting: None, 'text', 'stdout', 'stderr', + or custom C++ code. + + Raises + ------ + ValueError + If the report type is unsupported. + + Returns + ------- + str + The generated C++ source code for progress reporting. + """ # Code for a progress reporting function standard_code = """ std::string _format_time(float time_in_s) @@ -254,6 +425,45 @@ def get_report_func(self, report): def network_run(self, net, duration, report=None, report_period=10*second, namespace=None, profile=None, level=0, **kwds): + """ + Execute a Brian2 network simulation for the WASM backend. + + This method organizes network objects, generates C++ execution code, + and triggers the build if ``build_on_run`` is enabled. + + Parameters + ---------- + net : Network + The Brian2 network to simulate. + duration : Quantity + Duration of the simulation (must be non-negative). + report : str or None, optional + Progress reporting mode. Default is None. + report_period : Quantity, optional + Interval between progress reports. Default is 10*second. + namespace : dict, optional + Local namespace for variable resolution. Default is None. + profile : bool, optional + Whether to enable profiling. Default is None. + level : int, optional + Stack level for namespace detection. Default is 0. + **kwds : dict + Additional keyword arguments. + + Raises + ------ + ValueError + If duration is negative. + NotImplementedError + If multiple incompatible report functions are used. + RuntimeError + If the network was already built and run. + + Returns + ------- + None + Prepares and builds the simulation; does not return a value. + """ if duration < 0: raise ValueError( f"Function 'run' expected a non-negative duration but got '{duration}'" @@ -362,6 +572,33 @@ def network_run(self, net, duration, report=None, report_period=10*second, self.build(direct_call=False, **self.build_options) def run(self, directory, results_directory, with_output, run_args): + """ + Execute the compiled WASM simulation in a browser environment. + + This method launches the simulation using ``emrun`` and provides + browser-based progress reporting and visualization. + + Parameters + ---------- + directory : str + Build directory containing compiled files. + results_directory : str + Directory to store simulation results. + with_output : bool + Whether to forward stdout/stderr output. + run_args : list + Extra command-line arguments for the execution environment. + + Raises + ------ + RuntimeError + If the server cannot be launched or required files are missing. + + Returns + ------- + None + Runs the simulation in a browser; does not return a value. + """ html_file = self.build_options['html_file'] html_content = self.build_options['html_content'] if html_file is None: @@ -408,42 +645,54 @@ def run(self, directory, results_directory, with_output, run_args): def build(self, html_file=None, html_content=None, **kwds): """ - Build the project for the WASM backend. - - Parameters - ---------- - directory : str, optional - Target folder for the generated project. If ``None`` a temporary - directory is created. Default: ``"output"``. - results_directory : str, optional - Relative sub-folder used at runtime to store simulation results. - Default: ``"results"``. - compile : bool, optional - Compile the generated sources with *emcc*. Default: ``True``. - run : bool, optional - Execute the produced JavaScript/WASM bundle after a successful - build (headless node-style run). Default: ``True``. - debug : bool, optional - Add debug symbols and ``-g -DDEBUG`` flags. Default: ``False``. - clean : bool, optional - Remove previously compiled objects before building. Default: - ``False``. - with_output : bool, optional - Forward the program’s stdout/stderr when running. Default: - ``True``. - additional_source_files : list[str] | None - Extra ``.cpp`` files to compile alongside the generated Brian code. - run_args : list[str] | None - Additional command-line arguments passed to the executable HTML/ - JS harness when *run* is ``True``. - direct_call : bool, optional - ``True`` when invoked by user code, ``False`` when invoked - automatically because ``build_on_run=True``. - **kwds - Reserved for future keyword arguments; passing unknown names - raises ``TypeError``. - """ - + Build the project for the WASM backend. + + This method orchestrates the full build pipeline from code generation + to Emscripten compilation and optional execution. + + Parameters + ---------- + html_file : str, optional + Path to a custom HTML template file. + html_content : dict, optional + Dictionary of HTML template variables. + directory : str, optional + Target build directory. Defaults to "output". + results_directory : str, optional + Sub-folder for runtime results. Defaults to "results". + compile : bool, optional + Whether to compile sources with ``emcc``. Default is True. + run : bool, optional + Whether to run the generated bundle. Default is True. + debug : bool, optional + Whether to include debug flags. Default is False. + clean : bool, optional + Whether to clear old build artifacts. Default is False. + with_output : bool, optional + Whether to forward stdout/stderr. Default is True. + additional_source_files : list of str, optional + Extra ``.cpp`` files to include. + run_args : list of str, optional + Additional runtime arguments. + direct_call : bool, optional + True when called directly; False if triggered automatically. + **kwds : dict + Reserved for future options. + + Raises + ------ + RuntimeError + If build state is invalid or already executed. + TypeError + If results_directory is absolute. + ValueError + If invalid options are passed (e.g., negative threads). + + Returns + ------- + None + Produces a build directory with compiled WASM/HTML output. + """ self.build_options.update({'html_file': html_file, 'html_content': html_content}) @@ -509,6 +758,9 @@ def build(self, html_file=None, html_content=None, **kwds): + prefs["codegen.cpp.library_dirs"] + [d for c in self.code_objects.values() for d in c.compiler_kwds.get("library_dirs", [])] ) + # This library is only relevant when targetting Windows + if "advapi32" in self.libraries: + self.libraries.remove("advapi32") libraries = ( self.libraries + prefs["codegen.cpp.libraries"] @@ -573,7 +825,8 @@ def build(self, html_file=None, html_content=None, **kwds): ) if compile: - self.compile_source(directory, compiler, debug, clean) + # We switch the compiler name back to `mscv` on Windows, to make sure it uses `nmake` + self.compile_source(directory, 'msvc' if os.name == 'nt' else compiler, debug, clean) if run: self.run(directory, results_directory, with_output, run_args) diff --git a/brian2wasm/functions.py b/brian2wasm/functions.py index 7ed49d2..ee0d51d 100644 --- a/brian2wasm/functions.py +++ b/brian2wasm/functions.py @@ -16,7 +16,31 @@ ''') @check_units(i=1, t=second, result=1) def send_spike(i, t): - ''' - Send a spike message to JavaScript - ''' + """ + Send a spike message to JavaScript. + + This function injects JavaScript code via Emscripten's ``EM_ASM`` to send a spike event + message to the JavaScript environment, typically for visualization or processing in a + browser-based WebAssembly simulation. + + Parameters + ---------- + i : int + The index of the neuron or synapse emitting the spike. + t : Quantity + The time of the spike event, with units of seconds. + + Returns + ------- + float + A dummy return value of 0.0, as the function's primary effect is the JavaScript + message dispatch. + + Notes + ----- + This function is implemented in C++ using the ``@implementation`` decorator, which + defines a JavaScript ``postMessage`` call to send a message of type 'spike' with the + neuron index and time. The Python function body is a no-op (``pass``), as the actual + implementation is handled in C++ and executed in the WebAssembly environment. + """ pass \ No newline at end of file diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 0000000..d4bb2cb --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000..2166eb5 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,45 @@ +# Configuration file for the Sphinx documentation builder. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information +import os +import sys + +sys.path.insert(0, os.path.abspath("..")) + + +# -- Project information ----------------------------------------------------- + +project = 'brian2wasm' +copyright = '2025, Palash Chitnavis' +author = 'Palash Chitnavis' +release = '0.1.0' + +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + +extensions = [ + 'sphinx.ext.viewcode', + 'sphinx_rtd_theme', + "sphinx.ext.napoleon", + 'sphinxcontrib.mermaid', +] + +napoleon_google_docstring = False +napoleon_numpy_docstring = True +napoleon_include_private_with_doc = True +napoleon_include_special_with_doc = True + +templates_path = ['_templates'] +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + + + +# -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output + +html_theme = 'sphinx_rtd_theme' +html_static_path = ['_static'] \ No newline at end of file diff --git a/docs/developer/compilation.rst b/docs/developer/compilation.rst new file mode 100644 index 0000000..0c9f604 --- /dev/null +++ b/docs/developer/compilation.rst @@ -0,0 +1,152 @@ +Compilation and Code Generation Pipeline +======================================= + +Overview +-------- + +The brian2wasm compilation pipeline transforms Brian2 neural network simulations into WebAssembly modules for browser execution. The system consists of three integrated components: template-based code generation, cross-platform build system, and Emscripten compilation toolchain. + +Architecture +------------ + +The pipeline is orchestrated by the :code:`WASMStandaloneDevice` class, which inherits from Brian2's :code:`CPPStandaloneDevice` and redirects simulation execution toward WebAssembly compilation. + +.. mermaid:: + :align: center + + %%{init: {'theme': 'default', 'themeVariables': { 'fontSize': '10px'}}}%% + graph TB + + A[Brian2 Simulation Files] + B[array_specs] + C[dynamic_array_specs] + D[networks] + E[synapses] + F[clocks] + G["objects.cpp Template"] + H["objects.h"] + I["objects.cpp"] + J["C++ Functions"] + K["_init_arrays()"] + L["_load_arrays()"] + M["_write_arrays()"] + N["set_variable_by_name()"] + + A --> G + B --> G + C --> G + D --> G + E --> G + F --> G + G --> H + G --> I + I --> J + J --> K + J --> L + J --> M + J --> N + +Code Generation Templates +------------------------- + +Objects Template System +~~~~~~~~~~~~~~~~~~~~~~~ + +The :code:`objects.cpp` template generates comprehensive C++ simulation code from Brian2 objects. Key components include: + +* **Array Management**: Handles static arrays, dynamic arrays (1D/2D), and timed arrays with automatic memory allocation +* **Data Type Mapping**: Converts Brian2 types to C++ equivalents (:code:`double`, :code:`float`, :code:`int32_t`, :code:`int64_t`, :code:`char`) +* **Variable Setting**: Supports both scalar value assignment and binary file loading + +The template generates four core functions: + +* :code:`_init_arrays()`: Initialize simulation arrays with zero, arange, or file-based data +* :code:`_load_arrays()`: Load static arrays from binary files +* :code:`_write_arrays()`: Export results to browser via Emscripten interface +* :code:`set_variable_by_name()`: Runtime variable modification support + +Template Integration +~~~~~~~~~~~~~~~~~~~~ + +The device generates templates through the :code:`generate_objects_source()` method, passing simulation specifications including array specs, dynamic arrays, networks, and synapses. + +Build System +------------ + +Cross-Platform Makefile Generation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The build system generates platform-specific makefiles using Jinja2 templates: + +* **Unix/Linux**: Standard makefile with dependency tracking +* **Windows**: nmake-compatible makefile with explicit compilation rules + +Platform detection occurs in the :code:`generate_makefile()` method, which selects appropriate templates based on :code:`os.name`. + +Emscripten Configuration +~~~~~~~~~~~~~~~~~~~~~~~~ + +The build system configures Emscripten with optimized flags: + +**Optimization Flags**: +* :code:`-O3`: Maximum optimization +* :code:`-ffast-math`: Fast floating-point operations +* :code:`-fno-finite-math-only`: Preserve NaN/infinity handling + +**WebAssembly Flags**: +* :code:`-fwasm-exceptions`: Exception handling support +* :code:`-sALLOW_MEMORY_GROWTH`: Dynamic memory allocation +* :code:`-sMODULARIZE=1`: Modular WebAssembly generation +* :code:`-sENVIRONMENT=worker`: Web Worker compatibility + +EMSDK Management +~~~~~~~~~~~~~~~~ + +The system handles Emscripten SDK activation through preference-based configuration: + +* Automatic EMSDK path resolution from preferences or environment variables +* Conditional activation scripts for Unix systems +* Cross-platform compiler flag filtering to remove unsupported options + +Compilation Pipeline Flow +------------------------- + +1. **Device Activation**: :code:`WASMStandaloneDevice.activate()` configures templater and headers + +2. **Code Generation**: + * :code:`generate_objects_source()` creates C++ simulation code + * :code:`generate_makefile()` produces build configuration + * :code:`copy_source_files()` deploys runtime assets + +3. **Compilation**: Emscripten compiles C++ to WebAssembly with: + * Object file generation from source files + * Dependency tracking through :code:`make.deps` + * Final linking with JavaScript preamble and preloaded files + +4. **Runtime Integration**: Generated :code:`wasm_module.js` integrates with browser runtime through Web Workers + +Build Artifacts +--------------- + +The compilation produces: + +* :code:`wasm_module.js`: Main WebAssembly module with JavaScript interface +* :code:`wasm_module.wasm`: WebAssembly bytecode +* :code:`index.html`: Default web interface (auto-generated if not provided) +* Binary result files for simulation output +* Static array files for preloaded data + +Progress Reporting +------------------ + +The system implements C++ to JavaScript communication through Emscripten's :code:`EM_ASM` interface, enabling real-time progress updates during simulation execution. + +Configuration +------------- + +Key preferences control compilation behavior: + +* :code:`devices.wasm_standalone.emsdk_directory`: EMSDK installation path +* :code:`devices.wasm_standalone.emsdk_version`: EMSDK version selection + +The pipeline provides a seamless bridge from high-level Brian2 Python code to optimized WebAssembly execution in web browsers. \ No newline at end of file diff --git a/docs/developer/core.rst b/docs/developer/core.rst new file mode 100644 index 0000000..220e119 --- /dev/null +++ b/docs/developer/core.rst @@ -0,0 +1,96 @@ +Core Architecture +================= + +The :code:`brian2wasm` architecture is built around a custom Brian2 device that intercepts the normal simulation execution flow and redirects it to WebAssembly compilation. The system transforms Python/Brian2 code into C++, compiles it with Emscripten, and packages the result with JavaScript runtime components for browser execution. + +System Overview +--------------- + +.. mermaid:: + :align: center + + %%{init: {'theme': 'default', 'themeVariables': { 'fontSize': '7px'}}}%% + graph TB + + %% User Interface Layer + subgraph UI[User Interface Layer] + A[Python Script
.py files] + B[CLI Interface
__main__.py] + A --> B + end + + %% Core Processing Layer + subgraph CP[Core Processing Layer] + C[WASMStandaloneDevice
device.py] + D[Template System
brian2wasm templates] + E[Code Generation
objects.cpp, makefile] + C --> D + C --> E + end + + %% Compilation Layer + subgraph CL[Compilation Layer] + F[Static Assets
brian.js, worker.js] + G[Emscripten Toolchain
emcc, wasm-ld] + end + + %% Runtime Layer + subgraph RL[Runtime Layer] + H[WebAssembly Module
wasm_module.js] + I[Development Server
emrun] + J[Browser Environment] + end + + %% Connections + B --> C + D --> F + E --> G + F --> J + G --> H + H --> J + I --> J + +Device Architecture +------------------- + +**Key Device Methods:** + +* :code:`activate()` - Sets up the WASM-specific templater and headers +* :code:`generate_objects_source()` - Generates C++ source code with WASM-specific templates +* :code:`copy_source_files()` - Copies JavaScript runtime files to output directory + +Template and Asset Management +----------------------------- + +The device integrates with Brian2's templating system while adding WebAssembly-specific templates and runtime assets. + +**Core Components:** + +* :code:`objects.cpp` template - C++ simulation code generation +* :code:`makefile` / :code:`win_makefile` - Cross-platform build configuration +* :code:`worker.js` - Web Worker runtime +* :code:`brian.js` - Main JavaScript interface +* :code:`html_template` - Default web interface + +Configuration Management +------------------------ + +The system uses Brian2's preference system to manage Emscripten SDK configuration and build options. + +**Key Configuration Areas:** + +* EMSDK path resolution from environment variables +* HTML file configuration for custom interfaces +* Debug/release build flags +* Cross-platform compiler settings + +Cross-Platform Support +---------------------- + +The architecture includes robust cross-platform support with platform-specific adaptations: + +* **Windows**: MSVC filtering and :code:`win_makefile` template +* **Unix-like**: Standard flags and :code:`makefile` template +* **Server execution**: Platform-specific :code:`emrun` command handling + +The modular design allows for extension and customization while ensuring reliable cross-platform operation across Linux, macOS, and Windows environments. \ No newline at end of file diff --git a/docs/developer/index.rst b/docs/developer/index.rst new file mode 100644 index 0000000..6c3b6d1 --- /dev/null +++ b/docs/developer/index.rst @@ -0,0 +1,10 @@ +Developer's guide +============ + +.. toctree:: + :maxdepth: 2 + + core + wasm + compilation + runtime \ No newline at end of file diff --git a/docs/developer/runtime.rst b/docs/developer/runtime.rst new file mode 100644 index 0000000..13bba99 --- /dev/null +++ b/docs/developer/runtime.rst @@ -0,0 +1,145 @@ +Runtime System +============== + +The Runtime System provides the JavaScript interface layer that enables Brian2 simulations to execute within web browsers through WebAssembly modules. + +.. mermaid:: + :align: center + + %%{init: {'theme': 'default', 'themeVariables': { 'fontSize': '15px'}}}%% + graph TB + + subgraph WWT[Web Worker Thread] + A["worker.js
"] + B["WebAssembly Module
(wasm_module.js)"] + A --> B + end + + subgraph MT[Main Thread] + C["BrianSimulation Class
(brian.js)"] + D["HTML Interface
(progress bar, buttons)"] + E["Visualization
(Plotly/Chart.js)"] + C --> D + C --> E + end + + subgraph WASM[WASM Runtime Environment] + F["Emscripten File System"] + G["pre.js
(Module Configuration)"] + H["add_results() function"] + I["brian_result_object"] + F --> H + H --> I + G --> I + end + + %% Connections + A --> C + B --> G + +JavaScript Interface +-------------------- + +BrianSimulation Class +~~~~~~~~~~~~~~~~~~~~~ + +The core interface is implemented through the :code:`BrianSimulation` class, which manages simulation execution, progress reporting, and result visualization. + +The constructor accepts three optional parameters: + +- :code:`result_plots`: Array of plot configurations for visualization +- :code:`progress`: Progress reporting configuration (defaults to progress bar) +- :code:`run_button`: ID of the HTML run button element + +Initialization and Setup ++++++++++++++++++++++++ + +The :code:`init()` method configures DOM elements and establishes event handlers: + +Progress Reporting +++++++++++++++++++ + +Progress updates are handled through configurable reporting functions. The default 'bar' type updates both a progress bar element and text display: + +Result Visualization +++++++++++++++++++++ + +The system supports multiple plot types including raster plots for spike visualization: + +Custom plot functions can be registered through the :code:`result_plots` configuration. + +Simulation Execution +++++++++++++++++++++ + +The :code:`run()` method initiates simulation by disabling the run button and sending data to the web worker: + +Web Worker Extension +-------------------- + +Worker Implementation +~~~~~~~~~~~~~~~~~~~~~ + +The web worker handles WebAssembly module execution in a separate thread to maintain UI responsiveness: + +The worker accepts command-line style arguments and passes them to the WebAssembly module's main function. + +Message Communication ++++++++++++++++++++++ + +Communication between the main thread and worker uses a structured message protocol: + +Two message types are supported: + +- :code:`progress`: Real-time simulation progress updates +- :code:`results`: Final simulation data and results + +Web Template System +------------------ + +Module Configuration +~~~~~~~~~~~~~~~~~~~ + +The :code:`pre.js` template configures the Emscripten environment before WebAssembly execution: + +Data Transfer Functions ++++++++++++++++++++++++ + +The :code:`add_results()` function handles conversion of binary simulation data to JavaScript typed arrays: + +Supported data types include: + +- :code:`double` → :code:`Float64Array` +- :code:`float` → :code:`Float32Array` +- :code:`int32_t` → :code:`Int32Array` +- :code:`int64_t` → :code:`BigInt64Array` +- :code:`char` → :code:`Uint8Array` + +Console Integration ++++++++++++++++++++ + +Standard output and error streams are redirected to the browser console: + +Default HTML Template +-------------------- + +Template Configuration +~~~~~~~~~~~~~~~~~~~~~ + +The system generates default HTML interfaces when no custom template is provided. Default content includes configurable title, headers, and canvas dimensions: + +HTML Generation Process ++++++++++++++++++++++++ + +HTML files are automatically created during the build process if not explicitly provided: + +The template system uses the device's templater to generate complete HTML pages with embedded JavaScript runtime components. + +Asset Management +++++++++++++++++ + +Static assets including JavaScript files are copied to the project directory during compilation: + +Notes +----- + +The Runtime System integrates tightly with the Emscripten compilation pipeline and requires proper EMSDK configuration. Progress reporting uses Emscripten's :code:`EM_ASM` interface to bridge C++ simulation code with JavaScript event handling. The modular design allows for custom visualization functions and HTML templates while maintaining compatibility with the core Brian2 simulation framework. \ No newline at end of file diff --git a/docs/developer/wasm.rst b/docs/developer/wasm.rst new file mode 100644 index 0000000..7e3d976 --- /dev/null +++ b/docs/developer/wasm.rst @@ -0,0 +1,173 @@ +WASM Standalone Device +====================== + +Overview +-------- + +The :code:`WASM Standalone Device` is the core component of brian2wasm that enables running Brian2 neural network simulations in web browsers through WebAssembly compilation. The device inherits from Brian2's :code:`CPPStandaloneDevice` and overrides key methods to redirect simulation execution toward WebAssembly compilation instead of native C++ execution. + +**Architecture Overview** + +.. mermaid:: + :align: center + + classDiagram + class CPPStandaloneDevice { + +activate() + +build() + +network_run() + +generate_objects_source() + } + + class WASMStandaloneDevice { + -transfer_results : list + +transfer_only(variableviews) + +activate() + +generate_objects_source() + +generate_makefile() + +copy_source_files() + +get_report_func() + +network_run() + +run() + +build() + } + + class TemplateSystem { + +objects() + +makefile() + +win_makefile() + +html_template() + } + + class PreferenceSystem { + +emsdk_directory + +emsdk_version + } + + CPPStandaloneDevice <|-- WASMStandaloneDevice + WASMStandaloneDevice --> TemplateSystem : uses + WASMStandaloneDevice --> PreferenceSystem : configures + +Key Methods +~~~~~~~~~~~ + +**activate()** + Initializes the device by setting up the templater to use brian2wasm templates and ensuring :code:`emscripten.h` is included in headers. + +**generate_objects_source()** + Generates the main C++ source code for the simulation using the objects template with transfer results support. + +**generate_makefile()** + Creates platform-specific makefiles (Windows or Unix) with Emscripten-compatible compiler and linker flags. + +Configuration +------------- + +Preferences +~~~~~~~~~~~ + +The device uses Brian2's preference system for configuration: + +- :code:`devices.wasm_standalone.emsdk_directory`: Path to the Emscripten SDK directory +- :code:`devices.wasm_standalone.emsdk_version`: Version of EMSDK to use (defaults to "latest") + +The system also checks environment variables :code:`EMSDK` and :code:`CONDA_EMSDK_DIR` for SDK path resolution. + +HTML Integration +~~~~~~~~~~~~~~~~ + +The device supports custom HTML files and content for the web interface: + +- Default HTML template with configurable title, headers, description, and canvas dimensions +- Automatic HTML file generation if none exists +- Support for custom HTML files alongside Python scripts + +Progress Reporting +------------------ + +The device implements a sophisticated progress reporting system that bridges C++ simulation execution with JavaScript through Emscripten's :code:`EM_ASM` interface. + +The progress reporting includes: + +- Time formatting utilities for human-readable duration display +- Real-time progress updates sent to the web interface via :code:`postMessage` +- Configurable output streams (:code:`stdout`, :code:`stderr`, or custom) + +Network Execution +----------------- + +The :code:`network_run()` method orchestrates the simulation execution: + +1. Validates duration parameters +2. Processes network objects and code objects +3. Sets up progress reporting +4. Generates run lines for the simulation loop +5. Handles clock synchronization and caching + +File Management +--------------- + +The device manages several types of files during compilation: + +**Source Files** + Copies JavaScript runtime files (:code:`worker.js`, :code:`brian.js`) and HTML templates to the build directory. + +**Static Assets** + Handles preloading of static arrays and other simulation data. + +Platform Support +---------------- + +The device provides cross-platform support with platform-specific adaptations: + +**Windows** + Uses :code:`win_makefile` template and :code:`emrun` command directly. + +**Unix-like Systems** + Uses standard :code:`makefile` template and bash with :code:`emrun`. + +Compiler Flag Handling +~~~~~~~~~~~~~~~~~~~~~~ + +The device filters out incompatible compiler and linker flags: + +- Removes :code:`-march=native` from compiler flags +- Filters out MSVC-specific options +- Removes unsupported linker flags like :code:`--enable-new-dtags` and :code:`-R` + +Command Line Interface +---------------------- + +The device integrates with a CLI that automatically injects device setup code: + +The CLI supports: + +- Automatic device activation with :code:`set_device('wasm_standalone')` +- Custom HTML file detection and integration +- No-server mode for file generation without web server startup + +Usage Example +------------- + +To use the WASM Standalone Device in a Brian2 script:: + + from brian2 import * + import brian2wasm + + set_device('wasm_standalone', directory='my_simulation') + + # Your Brian2 simulation code here + # ... + + run(100*ms) + +Or via the command line:: + + python -m brian2wasm my_script.py + +Device Registration +------------------- + +The device is automatically registered with Brian2's device system: + +This allows it to be activated using Brian2's standard :code:`set_device()` function with the identifier :code:`'wasm_standalone'`. \ No newline at end of file diff --git a/docs/examples/IF_curve_LIF.rst b/docs/examples/IF_curve_LIF.rst new file mode 100644 index 0000000..5b0ee15 --- /dev/null +++ b/docs/examples/IF_curve_LIF.rst @@ -0,0 +1,233 @@ +Input-Frequency curve of a IF model +======================== + +Network: 1000 unconnected integrate-and-fire neurons (leaky IF) with an input parameter v0. The input is set differently for each neuron. + + .. code-block:: python + + // IF_curve_LIF.py + from brian2 import * + + n = 1000 + duration = 1*second + tau = 10*ms + eqs = ''' + dv/dt = (v0 - v) / tau : volt (unless refractory) + v0 : volt + ''' + group = NeuronGroup(n, eqs, threshold='v > 10*mV', reset='v = 0*mV', + refractory=5*ms, method='exact') + group.v = 0*mV + group.v0 = '20*mV * i / (n-1)' + + monitor = SpikeMonitor(group) + + run(duration, report='text', report_period=0.1*second) + + .. code-block:: html + + + + + + + + + Example: IF_curve_LIF + + + + + + +

+ Example: IF_curve_LIF +

+

+ Input-Frequency curve of a leaky integrate-and-fire (LIF) neuron model.
+ Network: 1000 unconnected integrate-and-fire neurons (leaky IF) with an input parameter v0.
+ The input is set differently for each neuron. +

+ + + +
+ + +
+ +
+ + + + + + .. code-block:: console + + python - m brian2wasm IF_curve_LIF.py + +**Output** + +.. image:: ../images/result_IF_curve_LIF.png \ No newline at end of file diff --git a/docs/examples/brunel_hakim.rst b/docs/examples/brunel_hakim.rst new file mode 100644 index 0000000..b6aff44 --- /dev/null +++ b/docs/examples/brunel_hakim.rst @@ -0,0 +1,48 @@ +Brunel Hakim 1999 +======================== + +Dynamics of a network of sparsely connected inhibitory current-based integrate-and-fire neurons. Individual neurons fire irregularly at low rate but the network is in an oscillatory global activity regime where neurons are weakly synchronized. + + +**Reference:** + “Fast Global Oscillations in Networks of Integrate-and-Fire Neurons with Low Firing Rates” Nicolas Brunel & Vincent Hakim Neural Computation 11, 1621-1671 (1999) + + .. code-block:: python + + // brunel_hakim1999.py + from brian2 import * + + N = 5000 + Vr = 10*mV + theta = 20*mV + tau = 20*ms + delta = 2*ms + taurefr = 2*ms + duration = .1*second + C = 1000 + sparseness = float(C)/N + J = .1*mV + muext = 25*mV + sigmaext = 1*mV + + eqs = """ + dV/dt = (-V+muext + sigmaext * sqrt(tau) * xi)/tau : volt + """ + + group = NeuronGroup(N, eqs, threshold='V>theta', + reset='V=Vr', refractory=taurefr, method='euler') + group.V = Vr + conn = Synapses(group, group, on_pre='V += -J', delay=delta) + conn.connect(p=sparseness) + M = SpikeMonitor(group) + LFP = PopulationRateMonitor(group) + + run(duration, report='text', report_period=0.1*second) + + .. code-block:: console + + python - m brian2wasm brunel_hakim1999.py + +**Output** + +.. image:: ../images/result_brunel_hakim1999.png \ No newline at end of file diff --git a/docs/examples/brunel_hakim_change_params.rst b/docs/examples/brunel_hakim_change_params.rst new file mode 100644 index 0000000..1bbc468 --- /dev/null +++ b/docs/examples/brunel_hakim_change_params.rst @@ -0,0 +1,95 @@ +Brunel Hakim 1999 (Interactive Parameters) +======================== + +This is an interactive version of the Brunel & Hakim (1999) model. +It allows users to change the external input mean (μ) and noise (σ) parameters directly in the browser, and re-run the simulation. + + +**Reference:** + “Fast Global Oscillations in Networks of Integrate-and-Fire Neurons with Low Firing Rates” Nicolas Brunel & Vincent Hakim Neural Computation 11, 1621-1671 (1999) + + .. code-block:: python + + // brunel_hakim1999_change_params.py + from brian2 import * + + N = 5000 + Vr = 10*mV + theta = 20*mV + tau = 20*ms + delta = 2*ms + taurefr = 2*ms + duration = .1*second + C = 1000 + sparseness = float(C)/N + J = .1*mV + muext = 25*mV + sigmaext = 1*mV + + eqs = """ + dV/dt = (-V+muext + sigmaext * sqrt(tau) * xi)/tau : volt + """ + + group = NeuronGroup(N, eqs, threshold='V>theta', + reset='V=Vr', refractory=taurefr, method='euler') + group.V = Vr + conn = Synapses(group, group, on_pre='V += -J', delay=delta) + conn.connect(p=sparseness) + M = SpikeMonitor(group) + LFP = PopulationRateMonitor(group) + + run(duration, report='text', report_period=0.1*second) + + .. code-block:: html + + + + + + + + Brian simulation: Brunel & Hakim (1999) + + + + + +

Fast Global Oscillations in Networks of Integrate-and-Fire Neurons with Low Firing Rates

+

Brunel & Hakim (1999)

+
+ +
+ + + 1 +
+ + + 25
+ + + + + .. code-block:: console + + python - m brian2wasm brunel_hakim1999_change_params.py + +**Output** + +.. image:: ../images/result_brunel_hakim1999_change_params.png \ No newline at end of file diff --git a/docs/examples/index.rst b/docs/examples/index.rst new file mode 100644 index 0000000..1c80b8f --- /dev/null +++ b/docs/examples/index.rst @@ -0,0 +1,11 @@ +Examples +============ + +.. toctree:: + :maxdepth: 2 + + brunel_hakim + brunel_hakim_change_params + IF_curve_LIF + non_reliabilty + ornstein_uhlenbeck \ No newline at end of file diff --git a/docs/examples/non_reliabilty.rst b/docs/examples/non_reliabilty.rst new file mode 100644 index 0000000..6d74d06 --- /dev/null +++ b/docs/examples/non_reliabilty.rst @@ -0,0 +1,233 @@ +Spike Timing Reliability +======================== + +Reliability of spike timing. + +See e.g. Mainen & Sejnowski (1995) for experimental results in vitro. + +Here: a constant current is injected in all trials. + + .. code-block:: python + + // non_reliability.py + from brian2 import * + + N = 24 + tau = 20*ms + sigma = .015 + eqs_neurons = ''' + dx/dt = (1.1 - x) / tau + sigma * (2 / tau)**.5 * xi : 1 (unless refractory) + ''' + neurons = NeuronGroup(N, model=eqs_neurons, threshold='x > 1', reset='x = 0', + refractory=5*ms, method='euler') + + + spikes = SpikeMonitor(neurons) + run(500*ms, report='text', report_period=0.1*second) + + .. code-block:: html + + + + + + + + Reliability of spike timing + + + + + + + +

Reliability of spike timing.

+

See e.g. Mainen & Sejnowski (1995) for experimental results in vitro.

+

Here: a constant current is injected in all trials.

+ + + + + +
+ +
+
+ + + + + .. code-block:: console + + python - m brian2wasm non_reliability.py + +**Output** + +.. image:: ../images/result_non_reliability.png \ No newline at end of file diff --git a/docs/examples/ornstein_uhlenbeck.rst b/docs/examples/ornstein_uhlenbeck.rst new file mode 100644 index 0000000..be6ffab --- /dev/null +++ b/docs/examples/ornstein_uhlenbeck.rst @@ -0,0 +1,241 @@ +Ornstein-Uhlenbeck process +======================== + +Ornstein-Uhlenbeck process + +Figure 2: Two realizations of the Ornstein-Uhlenbeck process for parameters τ=1.0 and σ=0.1 (black curve), and for τ=0.1 and σ=0.31622 (red curve). In both cases the noise intensity is σ^2*τ=0.01 . The red curve represents a noise that more closely mimics Gaussian white noise. Both realizations begin here at x(0)=1.0 , after which the mean decays exponentially to zero with time constant τ. + +Andre Longtin (2010) Stochastic dynamical systems. Scholarpedia, 5(4):1619. + +Sebastian Schmitt, 2022 + + .. code-block:: python + + // ornstein_uhlenbeck.py + from brian2 import * + + N = NeuronGroup( + 1, + """ + tau : second + sigma : 1 + dy/dt = -y/tau + sqrt(2*sigma**2/tau)*xi : 1 + """, + method="euler" + ) + + N.tau = 0.5 * second + N.sigma = 0.1 + N.y = 1 + + M = StateMonitor(N, "y", record=True) + + run(10 * second) + + .. code-block:: html + + + + + + + + Brian2 WASM Simulation + + + + + + + + + + + +

Brian2 WASM Simulation

+ +

Ornstein-Uhlenbeck Process Equation:

+

+ \[ + \frac{dy}{dt} = -\frac{y}{\tau} + \sigma \xi(t) + \] +

+ +
+ +
+ + + + 0.5 s +
+ + + + 0.9 +
+ + + + + + + + + .. code-block:: console + + python - m brian2wasm ornstein_uhlenbeck.py + +**Output** + +.. image:: ../images/result_ornstein_uhlenbeck.png \ No newline at end of file diff --git a/docs/images/access_deployment.png b/docs/images/access_deployment.png new file mode 100644 index 0000000..f95aad4 Binary files /dev/null and b/docs/images/access_deployment.png differ diff --git a/docs/images/actions_two_steps.png b/docs/images/actions_two_steps.png new file mode 100644 index 0000000..cb227a1 Binary files /dev/null and b/docs/images/actions_two_steps.png differ diff --git a/docs/images/result_IF_curve_LIF.png b/docs/images/result_IF_curve_LIF.png new file mode 100644 index 0000000..1a5f5d1 Binary files /dev/null and b/docs/images/result_IF_curve_LIF.png differ diff --git a/docs/images/result_brunel_hakim1999.png b/docs/images/result_brunel_hakim1999.png new file mode 100644 index 0000000..5f258bb Binary files /dev/null and b/docs/images/result_brunel_hakim1999.png differ diff --git a/docs/images/result_brunel_hakim1999_change_params.png b/docs/images/result_brunel_hakim1999_change_params.png new file mode 100644 index 0000000..adcfaac Binary files /dev/null and b/docs/images/result_brunel_hakim1999_change_params.png differ diff --git a/docs/images/result_non_reliability.png b/docs/images/result_non_reliability.png new file mode 100644 index 0000000..2ead389 Binary files /dev/null and b/docs/images/result_non_reliability.png differ diff --git a/docs/images/result_ornstein_uhlenbeck.png b/docs/images/result_ornstein_uhlenbeck.png new file mode 100644 index 0000000..3be96a4 Binary files /dev/null and b/docs/images/result_ornstein_uhlenbeck.png differ diff --git a/docs/images/setup_github_pages.png b/docs/images/setup_github_pages.png new file mode 100644 index 0000000..da63091 Binary files /dev/null and b/docs/images/setup_github_pages.png differ diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000..0ab8318 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,48 @@ +.. brian2wasm documentation master file, created by + sphinx-quickstart on Sun Aug 10 08:49:52 2025. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to Brian2WASM's Documentation +==================================== + +The ``brian2wasm`` package is a ``Brian 2`` simulator device that compiles ``Brian 2`` models into WebAssembly and JavaScript using the Emscripten toolchain. It generates a self-contained web folder containing HTML, JavaScript, and a ``.wasm`` binary, enabling simulations to run directly in any modern web browser. + +.. figure:: ./images/result_ornstein_uhlenbeck.png + :alt: Example simulation output + :align: center + + Example output of a ``brian2wasm`` simulation. + +Contributing +------------ + +We welcome contributions to ``brian2wasm``! If you're interested in contributing, please join the discussion on the `Brian Discourse Group `_. + +Bug Reports +----------- + +To report issues or suggest improvements, use the `GitHub issue tracker `_ or post in the `Brian Discourse Group `_. + +.. tip:: + Before submitting a bug report, check the `known issues `_ page to see if your issue is already documented. + +Contents +-------- + +.. toctree:: + :maxdepth: 2 + :titlesonly: + + user/index + developer/index + examples/index + +API Reference +------------- + +.. toctree:: + :maxdepth: 5 + :titlesonly: + + reference/brian2wasm \ No newline at end of file diff --git a/docs/make.bat b/docs/make.bat new file mode 100644 index 0000000..32bb245 --- /dev/null +++ b/docs/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=_build + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.https://www.sphinx-doc.org/ + exit /b 1 +) + +if "%1" == "" goto help + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/docs/reference/brian2wasm.__main__.rst b/docs/reference/brian2wasm.__main__.rst new file mode 100644 index 0000000..c8c51f8 --- /dev/null +++ b/docs/reference/brian2wasm.__main__.rst @@ -0,0 +1,7 @@ +brian2wasm.__main__ module +========================== + +.. automodule:: brian2wasm.__main__ + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/reference/brian2wasm.device.rst b/docs/reference/brian2wasm.device.rst new file mode 100644 index 0000000..3a5927a --- /dev/null +++ b/docs/reference/brian2wasm.device.rst @@ -0,0 +1,7 @@ +brian2wasm.device module +======================== + +.. automodule:: brian2wasm.device + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/reference/brian2wasm.rst b/docs/reference/brian2wasm.rst new file mode 100644 index 0000000..6cfd078 --- /dev/null +++ b/docs/reference/brian2wasm.rst @@ -0,0 +1,19 @@ +brian2wasm package +================== + +Submodules +---------- + +.. toctree:: + :maxdepth: 4 + + brian2wasm.device + brian2wasm.__main__ + +Module contents +--------------- + +.. automodule:: brian2wasm + :members: + :show-inheritance: + :undoc-members: diff --git a/docs/reference/modules.rst b/docs/reference/modules.rst new file mode 100644 index 0000000..7e9cf56 --- /dev/null +++ b/docs/reference/modules.rst @@ -0,0 +1,7 @@ +brian2wasm +========== + +.. toctree:: + :maxdepth: 4 + + brian2wasm diff --git a/docs/user/deploy.rst b/docs/user/deploy.rst new file mode 100644 index 0000000..86171b7 --- /dev/null +++ b/docs/user/deploy.rst @@ -0,0 +1,262 @@ +Deploying Your Simulation to GitHub Pages +======================================== + +You can deploy your ``brian2wasm`` simulation to `GitHub Pages `_, enabling others to view and interact with it directly in a web browser without any local installation. + +Prerequisites +------------- + +Ensure your repository includes: + +- Your ``brian2wasm`` simulation script (e.g., ``your_script.py``). +- The ``pyproject.toml`` file from the ``brian2wasm`` repository. +- A folder structure like this: + +.. code-block:: text + + 📁 root folder + ├─ 📁 .github + │ └─ 📁 workflows + │ └─ 📄 deploy.yml + ├─ 📁 brian2wasm + ├─ 📁 examples + ├─ 📄 your_script.py + └─ 📄 pyproject.toml + +.. note:: + Ensure all required files, including the simulation script and its corresponding HTML file (if customized), are committed to your repository. + +Setup Instructions +------------------ + +Follow these steps to deploy your simulation to GitHub Pages: + +1. **Create the GitHub Actions Workflow** + + Create a file named ``.github/workflows/deploy.yml`` in your repository with the following content: + + .. code-block:: yaml + + name: Deploy Brian2WASM Simulation to GitHub Pages + + on: + push: + branches: [ main ] + paths: + - ".github/workflows/deploy.yml" + - "**.py" + - "**.html" + + permissions: + contents: write + + jobs: + build-and-deploy: + runs-on: ubuntu-latest + + env: + SCRIPT: path/to/your/script.py # Replace with your script path + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Pixi + uses: prefix-dev/setup-pixi@v0.8.0 + with: + cache: false + + - name: Install dependencies + run: pixi install --locked + + - name: Run setup + run: pixi run setup + + - name: Derive folder paths + id: derive + run: | + # Remove .py extension + FOLDER_PATH="${SCRIPT%.py}" + # Get the last folder name (e.g., ornstein_uhlenbeck) + FOLDER_NAME=$(basename "$FOLDER_PATH") + echo "FOLDER_PATH=$FOLDER_PATH" >> $GITHUB_ENV + echo "FOLDER_NAME=$FOLDER_NAME" >> $GITHUB_ENV + + - name: Generate simulation files + run: | + python -m brian2wasm $SCRIPT --no-server + shell: pixi run bash -e {0} + + - name: Deploy to gh-pages + run: | + git fetch origin gh-pages || true + git checkout gh-pages || git checkout --orphan gh-pages + + rm -rf $FOLDER_NAME + mkdir -p $FOLDER_NAME + + cp $FOLDER_PATH/brian.js \ + $FOLDER_PATH/index.html \ + $FOLDER_PATH/wasm_module.js \ + $FOLDER_PATH/wasm_module.wasm \ + $FOLDER_PATH/worker.js \ + $FOLDER_NAME/ || true + + cat > index.html < + + + + ${GITHUB_ACTOR}'s Brian2WASM Simulations + + + +

${GITHUB_ACTOR}'s Brian2WASM Simulations

+
    + EOF + + for d in */ ; do + if [ -f "$d/index.html" ]; then + NAME=$(basename "$d") + echo "
  • $NAME
  • " >> index.html + fi + done + + cat >> index.html < + + + EOF + + git add $FOLDER_NAME index.html + git -c user.name='github-actions' -c user.email='github-actions@github.com' \ + commit -m "Deploy $FOLDER_NAME" || echo "No changes" + git push -f https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }} gh-pages + + .. note:: + This workflow triggers on pushes to the ``main`` branch when the workflow file, Python scripts, or HTML files are modified. + +2. **Configure Your Script Path** + + Update the ``SCRIPT`` environment variable in the ``deploy.yml`` file to point to your simulation script: + + .. code-block:: yaml + + env: + SCRIPT: examples/ornstein_uhlenbeck.py # Replace with your script path + + .. warning:: + Ensure the path is correct relative to the repository root. An incorrect path will cause the workflow to fail. + +3. **Commit and Push the Workflow** + + Add and commit the workflow file to your repository, then push it to the ``main`` branch: + + .. code-block:: bash + + git add .github/workflows/deploy.yml + git commit -m "Add GitHub Pages deployment workflow" + git push origin main + +4. **Enable GitHub Pages (First Time Only)** + + To enable GitHub Pages for your repository: + + a. Go to your repository on GitHub. + b. Click the **Settings** tab. + c. Navigate to **Pages** in the left sidebar. + d. Under **Source**, select **Deploy from a branch**. + e. Choose the **gh-pages** branch. + f. Select **/ (root)** as the folder. + g. Click **Save**. + + .. figure:: ../images/setup_github_pages.png + :alt: GitHub Pages setup screenshot + :align: center + + Screenshot of GitHub Pages configuration. + + .. note:: + This step is only required once per repository. + +5. **Monitor GitHub Actions** + + After pushing changes, check the **Actions** tab in your repository. Two workflows will run: + + - **Deploy Brian2WASM Simulation to GitHub Pages**: Builds your simulation files and pushes them to the ``gh-pages`` branch. + - **pages-build-deployment**: Publishes the ``gh-pages`` branch content to your GitHub Pages site. + + .. figure:: ../images/actions_two_steps.png + :alt: GitHub Actions workflows screenshot + :align: center + + Screenshot of GitHub Actions workflows. + + .. tip:: + If the workflow fails, check the error logs in the **Actions** tab to diagnose issues, such as incorrect script paths or missing files. + +6. **Access Your Deployed Simulation** + + Once the workflows complete, access your simulation in one of two ways: + + - Navigate to **Actions > Deployment > View deployment** and click the provided link. + - Visit directly: ``https://.github.io//`` + + Individual simulations are available at: ``https://.github.io///`` + + .. figure:: ../images/access_deployment.png + :alt: Accessing deployed simulation screenshot + :align: center + + Screenshot of deployment access options. + + .. note:: + For an example of a deployed simulation, see: https://palashchitnavis.github.io/brian2wasm/ + +Troubleshooting +--------------- + +Common Issues +~~~~~~~~~~~~~ + +- **Workflow fails to find script**: + Verify the ``SCRIPT`` path in ``deploy.yml`` is correct and relative to the repository root. + +- **Simulation not appearing**: + Ensure the simulation script and its corresponding HTML file (if customized) are committed to the repository. + +- **GitHub Pages not enabled**: + Confirm that GitHub Pages is configured to use the ``gh-pages`` branch in the repository settings. + +- **Missing output files**: + Check that ``brian.js``, ``wasm_module.js``, ``wasm_module.wasm``, and ``worker.js`` are generated correctly by the ``brian2wasm`` command. + +.. tip:: + Use the GitHub Actions logs to debug issues and ensure all required files are present in the ``gh-pages`` branch. \ No newline at end of file diff --git a/docs/user/html.rst b/docs/user/html.rst new file mode 100644 index 0000000..162b472 --- /dev/null +++ b/docs/user/html.rst @@ -0,0 +1,364 @@ +Writing HTML Files for Brian2WASM Scripts +======================================== + +This guide explains how to create custom HTML files to integrate with ``brian2wasm`` scripts, enabling tailored user interfaces, dynamic variable access, and interactive data visualizations. + +Overview +-------- + +When you run a ``Brian 2`` script (e.g., ``my_simulation.py``) with ``brian2wasm``, it automatically detects an HTML file with the same name (e.g., ``my_simulation.html``) in the same directory. This HTML file serves as the web interface for your simulation. + +.. note:: + If no matching HTML file is found, ``brian2wasm`` uses a default template with basic progress reporting and visualization features. + +.. tip:: + Ensure the HTML file has the same name as your Python script and is located in the same directory to avoid issues with detection. + +Basic HTML Structure +------------------- + +Your HTML file should include the following key components to ensure compatibility with ``brian2wasm``: + +Required JavaScript Libraries +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Include these libraries in the ```` section of your HTML: + +.. code-block:: html + + + + +.. note:: + The ``brian.js`` file is generated by ``brian2wasm`` and must be in the same directory as your HTML file. The Plotly library enables interactive visualizations. + +Essential HTML Elements +~~~~~~~~~~~~~~~~~~~~~~ + +Include these elements in the ```` for core functionality: + +.. code-block:: html + + +

    My Brian2 Simulation

    + + + + Ready to run + + + + + +
    + + +.. tip:: + Customize the styling (e.g., CSS) of these elements to match your desired interface design. + +Accessing Script Variables +------------------------- + +After the simulation completes, variables from your ``Brian 2`` script are accessible via the ``brian_results`` JavaScript object in the following structure: + +.. code-block:: javascript + + brian_results[owner_name][variable_name] + +Variable Access Examples +~~~~~~~~~~~~~~~~~~~~~~~ + +- For a spike monitor named ``spikemonitor``: + + .. code-block:: javascript + + // Access spike times + var spike_times = brian_results['spikemonitor'].t; + + // Access neuron indices + var neuron_indices = brian_results['spikemonitor'].i; + +- For a state monitor named ``statemonitor`` recording voltage: + + .. code-block:: javascript + + // Access recorded voltages + var voltages = brian_results['statemonitor'].v; + + // Access time points + var times = brian_results['statemonitor'].t; + +.. warning:: + Ensure your ``Brian 2`` script defines monitors with the correct names, as these are used to access data in ``brian_results``. Misnamed monitors will result in undefined variables. + +Setting Up the BrianSimulation Class +----------------------------------- + +Initialize the simulation interface in your HTML with JavaScript: + +.. code-block:: html + + + +.. note:: + The ``BrianSimulation`` class links your HTML elements to the simulation logic, enabling interaction and visualization. + +Built-in Plot Types +------------------ + +Raster Plots +~~~~~~~~~~~ + +For visualizing spike trains: + +.. code-block:: javascript + + var result_plots = [{ + type: 'raster', + canvas: 'my_canvas_id' + }]; + +.. note:: + Raster plots automatically use ``brian_results['spikemonitor'].t`` (spike times) and ``brian_results['spikemonitor'].i`` (neuron indices) for visualization. + +Custom Visualizations with Plotly +-------------------------------- + +Creating Custom Plot Functions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Define custom visualization functions for tailored plots: + +.. code-block:: javascript + + function custom_voltage_plot(event) { + var results = event.data.results; + var times = results['statemonitor'].t; + var voltages = results['statemonitor'].v; + + var trace = { + x: times, + y: voltages[0], // First neuron's voltage + type: 'scatter', + mode: 'lines', + name: 'Membrane Potential' + }; + + var layout = { + title: 'Neuron Voltage Over Time', + xaxis: { title: 'Time (s)' }, + yaxis: { title: 'Voltage (V)' } + }; + + Plotly.react('voltage_canvas', [trace], layout); + } + +Registering Custom Plots +~~~~~~~~~~~~~~~~~~~~~~~ + +Include custom functions in the ``result_plots`` configuration: + +.. code-block:: javascript + + var result_plots = [ + { + type: 'custom', + func: custom_voltage_plot + }, + { + type: 'raster', + canvas: 'spikes_canvas' + } + ]; + +Advanced Plotly Features +~~~~~~~~~~~~~~~~~~~~~~~ + +Multiple Traces +^^^^^^^^^^^^^^^ + +Plot data from multiple neurons: + +.. code-block:: javascript + + function multi_neuron_plot(event) { + var results = event.data.results; + var times = results['statemonitor'].t; + var voltages = results['statemonitor'].v; + + var traces = []; + for (let i = 0; i < 5; i++) { // Plot first 5 neurons + traces.push({ + x: times, + y: voltages[i], + type: 'scatter', + mode: 'lines', + name: `Neuron ${i}` + }); + } + + var layout = { + title: 'Multi-Neuron Voltages', + xaxis: { title: 'Time (s)' }, + yaxis: { title: 'Voltage (V)' } + }; + + Plotly.react('multi_canvas', traces, layout); + } + +Interactive Features +^^^^^^^^^^^^^^^^^^^ + +Add interactive elements like a range slider: + +.. code-block:: javascript + + var layout = { + title: 'Interactive Simulation Results', + xaxis: { + title: 'Time (s)', + rangeslider: {} // Enable range slider + }, + yaxis: { title: 'Value' } + }; + +Progress Reporting +----------------- + +The progress system provides real-time feedback during simulation execution. + +Progress Bar Configuration +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Configure a progress bar: + +.. code-block:: javascript + + var progress_config = { + type: 'bar', + bar_id: 'my_progress_bar', + text_id: 'my_progress_text' + }; + +.. warning:: + Currently, only the ``bar`` progress type is supported. Custom progress handlers are not yet available. + +Complete Example +--------------- + +Below is a complete example of an HTML file for a ``brian2wasm`` simulation: + +.. code-block:: html + + + + + Brian2 Simulation + + + + +
    +

    Neural Network Simulation

    + +
    + + + Ready to run +
    + +
    +

    Spike Raster Plot

    +
    +
    + +
    +

    Membrane Potential

    +
    +
    +
    + + + + + + +Troubleshooting +--------------- + +Common Issues +~~~~~~~~~~~~~ + +- **HTML file not detected**: + Ensure the HTML file has the same name as your Python script (e.g., ``simulation.py`` → ``simulation.html``) and is in the same directory. + +- **Variables not accessible**: + Verify that your ``Brian 2`` script defines monitors with the correct names. Use the browser's developer console (F12) to inspect the ``brian_results`` object. + +- **Plotly not loading**: + Check the Plotly CDN link (``https://cdn.plot.ly/plotly-latest.min.js``) for accessibility. For offline use, download Plotly and reference it locally. + +- **Progress bar not updating**: + Confirm that the progress bar and text elements have the correct IDs as specified in the ``progress_config`` object. + +.. tip:: + Use the browser's developer console (F12) to debug JavaScript errors and inspect the structure of the ``brian_results`` object for troubleshooting. \ No newline at end of file diff --git a/docs/user/index.rst b/docs/user/index.rst new file mode 100644 index 0000000..187d8fe --- /dev/null +++ b/docs/user/index.rst @@ -0,0 +1,11 @@ +User's guide +============ + +.. toctree:: + :maxdepth: 2 + + installation + usage + html + deploy + issues \ No newline at end of file diff --git a/docs/user/installation.rst b/docs/user/installation.rst new file mode 100644 index 0000000..f5ea360 --- /dev/null +++ b/docs/user/installation.rst @@ -0,0 +1,89 @@ +Installation Instructions +======================== + +The ``brian2wasm`` package is compatible with **Linux**, **macOS**, and **Windows**. It depends on the `Brian 2 simulator `_ and the `Emscripten `_ toolchain. Using the Pixi package manager is recommended, as it automatically handles these dependencies. + +Installation with Pixi +--------------------- + +`Pixi `_ is a modern, cross-platform package and environment manager that simplifies the setup of ``brian2wasm`` and its dependencies. + +To install and use ``brian2wasm`` with Pixi: + +1. **Install Pixi**: + + - For **Linux** or **macOS**: + + .. code-block:: bash + + curl -fsSL https://pixi.sh/install.sh | sh + + - For **Windows**: + + .. code-block:: powershell + + powershell -ExecutionPolicy Bypass -c "irm -useb https://pixi.sh/install.ps1 | iex" + + Alternatively, refer to the official `Pixi installation instructions `_. + +2. **Set up brian2wasm**: + + .. code-block:: bash + + git clone https://github.com/brian-team/brian2wasm.git + cd brian2wasm + pixi install # Installs Python, Emscripten, Brian 2, and other dependencies + pixi run setup # Activates Emscripten SDK (one-time step) + pixi shell # Enters the environment shell + + .. tip:: + Run ``pixi shell`` whenever you start a new session to access the configured environment. + + .. warning:: + Ensure you are in the ``brian2wasm`` directory before running ``pixi install`` to avoid setup errors. + +Installation with Conda (Experimental) +------------------------------------- + +.. warning:: + The Conda installation method is under development and may not be fully stable. Consider using Pixi for a more reliable experience. + +To install and use ``brian2wasm`` with Conda: + +1. **Create a Conda environment**: + + .. code-block:: bash + + conda create -n brian2wasm python + +2. **Activate the environment**: + + .. code-block:: bash + + conda activate brian2wasm + +3. **Install brian2wasm**: + + .. code-block:: bash + + conda install brian2wasm -c conda-forge + + .. note:: + Ensure the ``conda-forge`` channel is added to your Conda configuration. Run ``conda config --add channels conda-forge`` if needed. + +Installation with PIP +-------------------- + +If you have a pre-configured `Emscripten SDK (EMSDK) `_ installed, you can install ``brian2wasm`` using PIP. + +To install ``brian2wasm`` with PIP: + +.. code-block:: bash + + pip install brian2wasm + +.. warning:: + PIP installation requires a properly configured Emscripten SDK. Without it, the installation will fail. Refer to the `Emscripten documentation `_ for setup instructions. + +.. tip:: + Verify your Emscripten installation by running ``emcc --version`` before using PIP to install ``brian2wasm``. \ No newline at end of file diff --git a/docs/user/issues.rst b/docs/user/issues.rst new file mode 100644 index 0000000..ed77bab --- /dev/null +++ b/docs/user/issues.rst @@ -0,0 +1,18 @@ +Known Issues +============ + +Below is a list of known issues with ``brian2wasm``. For additional details or to report new issues, visit the `bug tracker on GitHub `_. + +- **Automatic Device Setting**: + When using the brian2wasm CLI tool, do not call ``set_device(...)`` in your Brian 2 script. The CLI automatically configures the device, and adding your own call may cause errors or unexpected behavior. + + However, if you are not using the CLI (e.g. running a script directly in Python), you can still manually set the device to ``wasm_standalone`` with ``set_device("wasm_standalone")``. + +- **Plotly CDN Dependency**: + The generated HTML files rely on Plotly charts that load assets from a CDN (``https://cdn.plot.ly/plotly-latest.min.js``). An internet connection is required for these charts to render properly. + +.. warning:: + If the CDN is inaccessible (e.g., due to network issues or restrictions), Plotly charts will not load. Consider hosting Plotly locally for offline use. + +.. tip:: + Check the GitHub issues page regularly for updates on fixes and workarounds for known problems. \ No newline at end of file diff --git a/docs/user/usage.rst b/docs/user/usage.rst new file mode 100644 index 0000000..d4f9b14 --- /dev/null +++ b/docs/user/usage.rst @@ -0,0 +1,54 @@ +Using Brian2WASM +================ + +``brian2wasm`` enables running standard ``Brian 2`` Python scripts, which are converted to WebAssembly for execution in a browser environment. + +Standard Usage +------------- + +To run a ``Brian 2`` script with ``brian2wasm``, use the following command: + +.. code-block:: bash + + python -m brian2wasm filename.py + +.. important:: + - The ``filename.py`` must be a valid ``Brian 2`` Python script. + - Do **not** include the ``set_device()`` function in your script, as ``brian2wasm`` automatically handles this. + +.. note:: + Ensure your script adheres to ``Brian 2`` syntax and conventions. Refer to the `Brian 2 documentation `_ for guidance. + +Command-Line Options +-------------------- + +``brian2wasm`` supports the following optional command-line flags to customize its behavior: + +1. **--no-server** + + .. code-block:: bash + + python -m brian2wasm filename.py --no-server + + .. important:: + - Generates WebAssembly and HTML output files (e.g., ``filename.html``, ``filename.js``, ``filename.wasm``) without launching a local preview server. + - Use this option if you want to manually serve or distribute the generated files. + + .. tip:: + This is useful for deploying the output to a web server or for further customization of the generated files. + +2. **--skip-install** + + .. code-block:: bash + + python -m brian2wasm filename.py --skip-install + + .. important:: + - Bypasses the check and activation of the Emscripten SDK (EMSDK). + - Use this only if the EMSDK is already configured and available in your environment (e.g., via Pixi, Conda, or a manual installation). + + .. warning:: + Running with ``--skip-install`` without a properly configured EMSDK will result in errors. Verify your EMSDK setup by running ``emcc --version`` before using this option. + +.. note:: + The generated WebAssembly files can be hosted on any standard web server or viewed locally by opening the ``filename.html`` file in a web browser. \ No newline at end of file diff --git a/examples/brunel_hakim1999_change_params.html b/examples/brunel_hakim1999_change_params.html index 01528c7..3d3147f 100644 --- a/examples/brunel_hakim1999_change_params.html +++ b/examples/brunel_hakim1999_change_params.html @@ -4,7 +4,7 @@ Brian simulation: Brunel & Hakim (1999) - + +