# Josh Josh is a domain-specific language (DSL) designed for ecological modeling with focus on vegetation and agent-based simulations. It enables the description of multi-occupancy patch-based ecological simulations where multiple species occupying grid cells can be modeled through individual behaviors with optional state changes. ## Purpose Support the nexus between science, policy, and engineering by executing vegetation-focused ecological simulations to aid management decisions. Prioritizes readability and interpretability for a broad audience including ecologists, policy makers, and researchers who may not have extensive software engineering backgrounds. ## Execution Environments Josh runs across multiple execution environments without requiring code changes: 1. **Browser via WebAssembly**: Complete simulations run directly in web browsers using WebAssembly compilation via TeaVM, requiring no local installation 2. **Local JVM**: High-performance execution on local machines using Java Virtual Machine for intensive computations and parallelization 3. **Distributed**: Either private or via community infrastructure, large-scale distributed processing across multiple machines with API key access ## Geospatial Data Preprocessing Josh preprocesses external geospatial data files into an optimized binary format called `.jshd` (Josh Data) files. The preprocessing workflow converts data from formats like GeoTIFF and NetCDF into a simulation optimized format, handling coordinate transformations, temporal alignment, and spatial resampling. Jshd files can be reused across replicates / trials. ## Command Line Interface (CLI) Josh provides a CLI through `JoshSimCommander` in `joshsim.jar`. ```bash java -jar joshsim-fat.jar [COMMAND] [OPTIONS] ``` All commands support these global options: - `--help`: Show command help - `--version`: Display version information - `--suppress-info`: Suppress standard output messages - `--suppress-errors`: Suppress error messages The CLI can be integrated with build systems and scripts: ```bash # Validate all Josh files in a directory find . -name "*.josh" -exec java -jar joshsim-fat.jar validate {} \; # Preprocessing pipeline java -jar joshsim-fat.jar preprocess simulation.josh Main temp.nc temperature K temp.jshd java -jar joshsim-fat.jar preprocess simulation.josh Main precip.nc precipitation mm precip.jshd java -jar joshsim-fat.jar run simulation.josh Main --data . -o results.csv ``` Each `.jshd` produced above is read from Josh code by its filename stem (e.g. `temp.jshd` → `external temp`). See the **External Data** section for the language-side syntax. ### `run` - Execute Simulations Runs a specified simulation from a Josh script file with support for replicates and various output formats. ```bash java -jar joshsim-fat.jar run [OPTIONS] ``` **Parameters:** - ``: Path to Josh simulation file - ``: Name of simulation to execute **Options:** - `--crs `: Coordinate Reference System (e.g., `EPSG:4326`) - `--csv-precision `: Max decimal places in CSV output, -1 for unlimited (default: 10) - `--custom-tag `: Custom template parameters; repeatable - `--data `: Path to directory containing external data files - `--enable-profiler`: Enable evalDuration profiling - `--export-queue-size `: Max records to buffer for backpressure (default: 1000000) - `-o, --output `: Output file path - `--output-format `: Output format (csv, netcdf, geotiff) - `--output-steps `: Comma-separated list of time steps to export (e.g., 5,7,8,9,20) - `--parallel`: Enable parallel patch processing - `--replicate-index `: Run single replicate (mutually exclusive with --replicates); used by K8s indexed Jobs - `--replicates `: Number of replicates (default: 1) - `--seed `: Random seed for reproducibility; forces serial patch execution - `--serial-patches`: Run patches serially - `--upload-config`: Upload .jshc files to MinIO after completion - `--upload-data`: Upload .jshd files to MinIO after completion - `--upload-source`: Upload .josh file to MinIO after completion - `--use-float-64`: Use float64 instead of BigDecimal **Examples:** ```bash # Run with random seed for deterministic results java -jar joshsim-fat.jar run simulation.josh Main --seed 42 -o results.csv # Export only specific timesteps java -jar joshsim-fat.jar run simulation.josh Main --output-steps 5,7,8,9,20 -o results.csv # Custom template parameters java -jar joshsim-fat.jar run simulation.josh Main --custom-tag environment=test --custom-tag version=v2.1 # Limit CSV precision java -jar joshsim-fat.jar run simulation.josh Main --csv-precision 4 -o results.csv ``` ### `preprocess` - Data Preprocessing Converts external geospatial data files into optimized binary `.jshd` format reusable across runs (trials / replicates). Output can also be written as `.jshdz` (XZ/LZMA2-compressed JSHD) by using that extension — **JVM only**, not supported in the browser editor. The output filename's stem becomes the identifier referenced from Josh code via the `external` keyword; see the **External Data** section. ```bash java -jar joshsim-fat.jar preprocess [OPTIONS] ``` **Parameters:** - ``: Path to Josh simulation file - ``: Name of simulation for preprocessing - ``: Path to input data file (NetCDF, GeoTIFF, etc.) - ``: Variable name or band number to extract - ``: Units of the data for simulation use - `` or ``: Path for output preprocessed file. Use `.jshdz` extension to produce an XZ-compressed file (significantly smaller, JVM only). **Options:** - `--amend`: Amend existing JSHD/JSHDZ file (adds new timestep) - `--crs `: Coordinate Reference System (e.g., EPSG:4326) - `--custom-tag `: Custom template parameters; repeatable - `--data `: Path to directory containing external data files - `--default-value `: Default value for grid spaces before copying - `--parallel`: Enable parallel patch processing - `--serial-patches`: Run patches serially - `--time-dim `: Time dimension name - `--timestep `: Process single timestep - `--use-float-64`: Use double precision instead of BigDecimal - `--x-coord `: X coordinate dimension name - `--y-coord `: Y coordinate dimension name **Examples:** ```bash # Basic preprocessing java -jar joshsim-fat.jar preprocess simulation.josh Main temperature.nc temp K temperature.jshd # Preprocessing to compressed .jshdz format (JVM only) java -jar joshsim-fat.jar preprocess simulation.josh Main temperature.nc temp K temperature.jshdz # Preprocessing with custom coordinates java -jar joshsim-fat.jar preprocess simulation.josh Main precip.nc rainfall mm precipitation.jshd --x-coord longitude --y-coord latitude # Parallel preprocessing for faster execution on multi-core machines java -jar joshsim-fat.jar preprocess simulation.josh Main precip.nc rainfall mm precip.jshd --parallel ``` ### `validate` - Syntax Validation Validates Josh script syntax and reports parsing errors without executing simulations. ```bash java -jar joshsim-fat.jar validate ``` **Parameters:** - ``: Path to Josh script file to validate **Options:** - `--upload-source`: Upload source .josh file to MinIO after validation (requires MinIO configuration) ### `discoverConfig` - Configuration Discovery Analyzes Josh scripts to discover all configuration variables used, helping generate appropriate `.jshc` files. ```bash java -jar joshsim-fat.jar discoverConfig ``` **Parameters:** - ``: Path to Josh script file to analyze ### `inspectJshd` - Data Inspection Inspects values in preprocessed JSHD or JSHDZ files at specific coordinates and timesteps for debugging and validation. Both `.jshd` (uncompressed) and `.jshdz` (XZ-compressed, JVM only) files are supported. ```bash java -jar joshsim-fat.jar inspectJshd ``` **Parameters:** - `` or ``: Path to JSHD or JSHDZ file to inspect - ``: Variable name to examine - ``: Time step to inspect - ``: X coordinate (grid space) - ``: Y coordinate (grid space) **Options:** - `--to-csv `: Export the entire grid across all timesteps to a CSV file instead of inspecting a single value. When used, ``, ``, and `` are not required. The CSV contains columns `x,y,timestep,value`. Grid metadata is printed to stdout as JSON: ```json { "minX": 0, "maxX": 922, "minY": 0, "maxY": 1112, "minTimestep": 0, "maxTimestep": 1, "width": 923, "height": 1113, "units": "K", "csv": "/path/to/output.csv" } ``` ### `inspect-exports` - Export Path Extraction Parses a Josh script and extracts the configured export and debug file paths from a simulation without running it. Useful for build systems, CI/CD pipelines, and tooling that needs to know where simulation outputs will be written. ```bash java -jar joshsim-fat.jar inspect-exports [OPTIONS] ``` **Parameters:** - ``: Path to Josh script file to inspect - ``: Name of simulation to extract paths from **Options:** - `--json`: Output in JSON format (default: true), otherwise plain text ### `mcp` - Model Context Protocol (MCP) Server Runs Josh as a [Model Context Protocol](https://modelcontextprotocol.io) server over stdio, letting LLM clients (Claude Desktop, opencode, etc.) drive Josh against files on the local disk. This is the recommended way for an AI agent to operate Josh. ```bash java -jar joshsim-fat.jar mcp ``` MCP exposes four tools, each operating on local file paths: - `validate_simulation`: Parse and validate a `.josh` script; returns parse errors with locations. - `discover_config`: List the `config`-referenced variables a script expects (i.e. what a `.jshc` file must provide). - `preprocess_data`: Convert a NetCDF/GeoTIFF/`.jshd` file into a grid-aligned `.jshd` (wraps the `preprocess` command). - `run_simulation`: Run a named simulation, writing the exports defined in the script (wraps the `run` command). Accepts an optional `data` object mapping each external resource name the script references (via `external `, e.g. `"temperature.jshd"`) to a `.jshd`/`.jshdz` file path — the equivalent of the CLI `--data` flag. When omitted, external data is resolved by filename from the working directory. All tools run in the local JVM and behave identically to the equivalent CLI command. MinIO credentials and any API keys are read from the environment, never passed as tool arguments (keeping secrets out of the LLM's context). Example opencode configuration (`opencode.json`): ```json { "mcp": { "josh": { "type": "local", "command": ["java", "-jar", "/path/to/joshsim-fat.jar", "mcp"] } } } ``` ### `server` - HTTP Server Mode Starts Josh as an HTTP server for web-based simulation execution and API access. ```bash java -jar joshsim-fat.jar server [OPTIONS] ``` **Options:** - `--concurrent-workers `: Number of concurrent workers allowed (default: 1) - `--host `: Server host (default: localhost) - `--port `: Server port (default: 8080) - `--serial-patches`: Run patches in serial instead of parallel - `--use-http2`: Enable HTTP/2 support (streaming) - `--worker-url `: URL for worker requests (default: http://0.0.0.0:8085/runReplicate) API keys via `JOSH_API_KEYS` environment variable (comma-separated list). If not set or empty, all API keys are allowed. This opens the following endpoints (it is recommended not to access these directly and, instead, use the jar): - `/health`: Returns 200 with "healthy" text - `/parse`: POST Josh script, returns JSON with parsed AST or errors - `/discoverConfig`: POST Josh script, returns JSON with config variable names - `/runReplicate`: POST to run single replicate with JSON body, streams results - `/runReplicates`: Leader endpoint for distributed replicate execution - `/runBatch`: POST batch job to MinIO-staged target, returns job ID - `/preprocessBatch`: POST preprocessing job to MinIO-staged target, returns job ID ### `runRemote` - Cloud Execution Executes simulations on Josh Cloud infrastructure for distributed processing. ```bash java -jar joshsim-fat.jar runRemote [OPTIONS] ``` **Parameters:** - ``: Path to Josh simulation file - ``: Name of simulation to execute **Options:** - `--api-key `: API key for authentication (required for Josh Cloud and self-hosted server running with API keys) - `--concurrent-workers `: Number of concurrent workers allowed (default: 1) - `--custom-tag `: Custom template parameters (format: name=value); can be specified multiple times - `--data `: Path to directory containing external data files - `--endpoint `: Custom endpoint (see `server` command) - `--replicates `: Number of replicates - `--remote-leader `: Remote leader URL for distributed execution - `--upload-config`: Upload configuration .jshc files to MinIO after completion (requires MinIO configuration) - `--upload-data`: Upload data .jshd files to MinIO after completion (requires MinIO configuration) - `--upload-source`: Upload source .josh file to MinIO after completion (requires MinIO configuration) - `--use-float-64`: Use double precision instead of BigDecimal for speed vs precision tradeoff ### Batch commands Batch commands enable large-scale distributed batch simulation execution against MinIO-staged inputs on remote HTTP/Kubernetes targets with polling and replication support. Ask the user to confirm before taking advantage of these as, unlike other commands which may optionally use MinIO, batch commands require MinIO. #### Batch targets Target profiles (`~/.josh/targets/.json`) define remote compute endpoints for batch execution. Two target types are supported: **HTTP targets** (Cloud Run or self-hosted server): Uses MinIO polling via `status.json` files written to object storage. All replicates run sequentially in a single container. **Kubernetes targets** (GKE, EKS, self-hosted): Creates indexed Jobs with one pod per replicate for parallel execution. Polls Job API directly. MinIO credentials are resolved through a hierarchy: profile JSON takes priority over environment variables. Secrets do not need to live in the profile file. **Workflow:** Inputs are staged to MinIO, then job is dispatched to target which polls MinIO's `status.json` for progress. Using `--no-wait` returns job ID as JSON immediately without polling. **HTTP target profile example:** ```json { "type": "http", "http": { "endpoint": "https://your-cloudrun-url.run.app", "apiKey": "your-api-key" }, "minio_endpoint": "https://storage.googleapis.com", "minio_bucket": "your-bucket" } ``` **Kubernetes target profile example:** ```json { "type": "kubernetes", "kubernetes": { "context": "gke_project_region_cluster", "namespace": "joshsim", "image": "ghcr.io/schmidtdse/josh/joshsim-batch:latest", "pod_minio_endpoint": "https://storage.googleapis.com", "resources": { "requests": { "cpu": "1", "memory": "2Gi" }, "limits": { "memory": "4Gi" } }, "parallelism": 5, "timeoutSeconds": 600, "ttlSecondsAfterFinished": 3600, "spot": true }, "minio_endpoint": "https://storage.googleapis.com", "minio_bucket": "your-bucket" } ``` K8s target options include `spot` for preemptible VMs (typically 60-90% cost savings), `ttlSecondsAfterFinished` for automatic Job cleanup after a configurable time period, `nodeSelector` for node selection, `pod_minio_endpoint` for custom MinIO endpoints, and `jarPath` to override the default jar location (`/app/joshsim-fat.jar`). #### `batchRemote` - Batch Execution on Remote Targets Dispatches batch simulations to remote compute targets (HTTP or Kubernetes) against MinIO-staged inputs. ```bash java -jar joshsim-fat.jar batchRemote --target --minio-prefix [OPTIONS] ``` **Parameters:** - ``: Name of simulation to execute **Options:** - `--custom-tag `: Custom template parameters (format: `name=value`); can be specified multiple times; resolvable as `{name}` in exportFiles paths - `--minio-prefix `: MinIO object prefix where inputs live (e.g., `batch-jobs/my-run/inputs/`) (required) - `--no-wait`: Dispatch and exit without polling for completion; returns job ID as JSON - `--poll-interval `: Seconds between status polls (default: 5) - `--replicate-start `: Starting replicate index (default: 0); combined with `--replicates` selects range `[start, start+count)` - `--replicates `: Number of replicates to execute (default: 1) - `--require-prestaged`: Fail fast unless `.josh-staged.json` at `--minio-prefix` reports 'complete' - `--target `: Target profile name (loads `~/.josh/targets/.json`) (required) - `--timeout `: Maximum seconds to wait for completion (default: 3600) **Examples:** ```bash # Dispatch 50 replicates against pre-staged inputs java -jar joshsim-fat.jar batchRemote Main --target=nautilus \ --minio-prefix=batch-jobs/my-run/inputs/ \ --require-prestaged --replicates=50 # Dispatch and return immediately (no polling) java -jar joshsim-fat.jar batchRemote Main --target=nautilus \ --minio-prefix=batch-jobs/my-run/inputs/ --no-wait ``` #### `preprocessBatch` - Remote Data Preprocessing Dispatches geospatial data preprocessing to remote compute targets via MinIO staging, then downloads the resulting `.jshd` file. ```bash java -jar joshsim-fat.jar preprocessBatch --target [OPTIONS] ``` **Parameters:** - ``: Path to Josh simulation file or input directory containing data files - ``: Name of simulation for grid/metadata extraction - ``: Data file name within the input directory - ``: Variable name or band number to extract - ``: Units of the data for simulation use - ``: Local output path for the resulting `.jshd` file **Options:** - `--amend`: Amend existing JSHD file rather than overwriting - `--crs `: Coordinate Reference System for reading input file (default: EPSG:4326) - `--default-value `: Default value to fill grid spaces before copying data from source file - `--no-wait`: Dispatch and exit without polling for completion - `--parallel`: Enable parallel processing of patches within each timestep - `--poll-interval `: Seconds between status polls (default: 5) - `--target `: Target profile name (loads `~/.josh/targets/.json`) (required) - `--timeout `: Maximum seconds to wait for completion (default: 3600) - `--time-dim `: Name of time dimension (default: calendar_year) - `--timestep `: Process only a single timestep - `--x-coord `: Name of X coordinate dimension (default: lon) - `--y-coord `: Name of Y coordinate dimension (default: lat) **Examples:** ```bash # Preprocess NetCDF to jshd on remote Kubernetes target java -jar joshsim-fat.jar preprocessBatch ./sim simulation.josh temp.nc temperature K temp.jshd --target=k8s # Dispatch and return immediately (no polling) java -jar joshsim-fat.jar preprocessBatch ./sim simulation.josh temp.nc temperature K temp.jshd --target=http --no-wait ``` #### `stageToMinio` - MinIO Directory Staging Uploads all files in a local directory to a MinIO prefix, preserving directory structure. Used to stage inputs for batch jobs. ```bash java -jar joshsim-fat.jar stageToMinio --input-dir --prefix [OPTIONS] ``` **Options:** - `--input-dir `: Local directory to upload (required) - `--prefix `: MinIO object prefix (e.g., `batch-jobs/abc/inputs/`) (required) **Examples:** ```bash # Stage simulation inputs to MinIO java -jar joshsim-fat.jar stageToMinio --input-dir=./sim/ --prefix=batch-jobs/abc/inputs/ # Stage with custom prefix java -jar joshsim-fat.jar stageToMinio --input-dir=./sim/ --prefix=batch-jobs/my-run/inputs/ ``` #### `stageFromMinio` - MinIO Directory Retrieval Downloads all files under a MinIO prefix to a local directory. Used on workers to retrieve staged simulation inputs before running. ```bash java -jar joshsim-fat.jar stageFromMinio --prefix --output-dir [OPTIONS] ``` **Options:** - `--prefix `: MinIO object prefix to download from (e.g., `batch-jobs/abc/inputs/`) (required) - `--output-dir `: Local directory to download files into (required) **Examples:** ```bash # Download staged inputs from MinIO java -jar joshsim-fat.jar stageFromMinio --prefix=batch-jobs/abc/inputs/ --output-dir=./work/ ``` #### `pollBatch` - Batch Job Status Polling Checks the status of a previously dispatched batch job. Outputs JSON status to stdout. ```bash java -jar joshsim-fat.jar pollBatch --target ``` **Parameters:** - ``: Job ID returned by `batchRemote --no-wait` or `preprocessBatch --no-wait` **Options:** - `--target `: Target profile name (loads `~/.josh/targets/.json`) (required) **Output:** JSON object with status, jobId, and timestamp fields. Exit codes: 0 = complete, 1 = error, 2 = running/pending. **Examples:** ```bash # Check job status java -jar joshsim-fat.jar pollBatch 550e8400-e29b-41d4-a716-446655440000 --target=nautilus ``` ## Configuration Files (.jshc) Josh supports external configuration files using the `.jshc` format for parameterizing simulations without modifying Josh code. This enables users to modify simulation parameters without changing Josh source code. ### Format Specification - One variable per line: `variableName = value units` - Comments using `#` character (all text after # ignored until newline) - Empty lines allowed and ignored - Whitespace flexible around equals sign (zero or more spaces/tabs) - Variable names follow Josh identifier pattern: `[A-Za-z][A-Za-z0-9]*` - Values must be valid EngineValue format (number + optional units) ### Usage in Josh Code - Access via `config configName.variableName` expressions - Example: `config example.testVar1` references `testVar1` from `example.jshc` - Files loaded from working directory or provided with HTTP request - Values are constant across all timesteps and spatial locations - Configuration variables can be discovered using `discoverConfig` command ### Example Configuration File (example.jshc) ``` # Group 1 - Tree parameters testVar1 = 5 meters testVar2 = 10m # Group 2 - Environment parameters testVar3 = 15 km ``` ### Commands The `discoverConfig script.josh` lists all configuration variables used in a Josh script. Variables discovered can be used to generate appropriate .jshc files. ## Language Structure Josh uses a stanza-based approach. Each stanza defines an entity (object) inside `start` / `end` blocks. The language is imperative with procedural elements. ### Entity Types - **simulation**: Defines simulation parameters, grid specifications, time steps, and export configurations - **patch**: Represents spatial cells containing organisms and environmental conditions - **organism**: Individual agents with attributes and behaviors that change over time - **disturbance**: Events that affect organisms or patches (e.g., fire, drought) - **management**: Intentional interventions and management actions - **external**: References to external data sources and resources - **unit**: Custom unit definitions with conversions and aliases ### Stanza Structure Each entity stanza follows this pattern: ``` start . = .: = end ``` ### Events Event handlers are run once per variable and may be made conditional based on values on the entity. When a variable is referenced in an expression, its value will be resolved if it is not yet evaluated, automatically creating a computational graph such that the ordering of event handlers does not matter. - **init**: One-time setup event. For patches and the simulation, runs at the first timestep only, before any `start`/`step`/`end`. For organisms created mid-simulation, runs once at creation. Use it to bootstrap state that `prior.X` recurrences read, to sample fixed initial conditions, or to `create` initial organisms. - **start**: Runs at the beginning of every timestep (after `init` on the first one). Use it for per-step pre-processing such as culling dead entities. - **step**: Main per-timestep event. Most attribute updates live here. - **end**: Runs at the end of every timestep. Use it for per-step post-processing such as creating new organisms based on the step's results. Within a single timestep, substeps fire in the order `init` (first timestep only) → `start` → `step` → `end`. An attribute with no `.step` handler retains its last value (typically the one set by `.init`), so define handlers only for the events whose semantics you actually need. ## Language A complete Josh program consists of: 1. Optional configuration statements 2. Optional import statements 3. Simulation definition 4. Other entity stanzas (patch, organism, etc.) 5. Unit definitions Example minimal program: ``` start simulation Main grid.size = 1000 m grid.low = 33.7 degrees latitude, -115.4 degrees longitude grid.high = 34.0 degrees latitude, -116.4 degrees longitude steps.low = 0 count steps.high = 10 count end simulation start patch Default ExampleTree.init = create 10 count of ExampleTree end patch start organism ExampleTree age.init = 0 year age.step = prior.age + 1 year end organism start unit year alias years end unit ``` ### Spatial / Temporal Configuration Set on the `simulation` stanza: ``` grid.size = 1000 m grid.low = 35.5 degrees latitude, -120.0 degrees longitude grid.high = 34.5 degrees latitude, -119.0 degrees longitude grid.patch = "Default" steps.low = 0 count steps.high = 10 count ``` - **`grid.low`** (alias **`grid.top_left`**) — top-left / north-west grid corner; **`grid.high`** (alias **`grid.bottom_right`**) — bottom-right / south-east grid corner. - Each corner component must carry the word `latitude` or `longitude` (parser tags, not units); order within the pair is free. - **`grid.size`** — patch side length. Units: `m` (meters), `count`, or `degrees`. When positions are given in degrees, the size must be in meters, which triggers a Haversine conversion; `here.x` / `here.y` then reference grid space `(0,0)` top-left to `(width, height)` bottom-right. Other length units such as `km` are not accepted for grid size — use meters (e.g. `1000 m`, not `1 km`). - **`grid.patch`** — patch entity name (default `"Default"`). - **`grid.inputCrs` / `grid.targetCrs`** — optional EPSG codes; the CLI `--crs` flag overrides `grid.inputCrs`. - **`steps.low` / `steps.high`** — both endpoints inclusive (`0` to `10` runs 11 timesteps; defaults `0`, `10`). ### Comments Single-line comments use `#` syntax: ``` # This is a comment ``` ### Data Types **Numeric values with optional units:** ``` 5 meters 10.5 years 42 count 33.7 degrees latitude ``` A unit must attach to a number. Write a rate or compound unit as a value-over-value division or a quoted compound — `300 mm / 1 year` or `300 "mm / year"` — not `300 / year` (a bare unit name with no number is treated as an undefined value, not a unit). **String literals enclosed in double quotes:** ``` "Default" "file:///tmp/output.csv" ``` **Boolean values:** ``` true false ``` **Statistical distributions for stochastic modeling:** - **uniform**: Uniform distribution sampling between min and max values (`uniform from 0 meters to 1 meters`) - **normal**: Normal (Gaussian) distribution with specified mean and standard deviation (`normal with mean of 10 years std of 2 years`) - **binomial**: Binomial distribution for modeling success/failure outcomes with `n` trials and success probability `p` (`binomial with n of 20 count p of 0.5 count`) ### Unit System Units are strongly typed and support conversions: #### Built-in Units - **Percentage**: `%`, `percent` - **Count**: `count`, `counts` - **Degrees**: `degrees`, `degree` (for latitude/longitude) - **Meters**: `meter`, `meters`, `m` - **Kilometers**: `kilometer`, `kilometers`, `km` - **Boolean**: `bool` - **String**: `string` #### Custom Units ``` start unit year alias years alias yr alias yrs end unit ``` #### Unit Conversions Use `current` to refer to the value being converted. The LHS of `=` names the target unit; the RHS is the conversion expression. ``` start unit inch meter = current * 0.0254 end unit ``` ### Identifiers and Attributes Identifiers follow the pattern `[A-Za-z][A-Za-z0-9]*` and can be chained with dot notation: ``` organism.attribute patch.ExampleTree.age current.height ``` ### Keywords and Context #### Temporal Keywords - **current**: Current state of entity - **prior**: State from previous timestep - **meta**: Simulation metadata and context - `meta.stepCount`: Current simulation timestep (0-based) - `meta.year`: Current simulation year #### Spatial Keywords - **here**: Current grid cell context - **within**: Spatial query for nearby entities **Examples:** ``` # Count patches within 250m radius at prior timestep nNearby.step = count(Default within 250 m radial at prior) # Access current patch coordinates patch.info.step = "Patch at (" | here.x | ", " | here.y | ")" ``` ### Operators Arithmetic: - `+`: Addition - `-`: Subtraction - `*`: Multiplication - `/`: Division - `^`: Exponentiation - `%`: Modulo Comparison: - `==`: Equality - `!=`: Inequality - `>`: Greater than - `>=`: Greater than or equal - `<`: Less than - `<=`: Less than or equal Logical: - `and`: Logical AND - `or`: Logical OR - `xor`: Logical XOR String: - `|`: String concatenation ### Functions ``` function_name(arguments) mean(ExampleTree.age) sample(uniform from 0 to 10) ``` Built-in: - **mean()**: Calculate mean of collection - **std()**: Calculate standard deviation - **count()**: Count elements in collection - **sample**: Sample from a distribution or collection — see the **Sampling** subsection below for full syntax. - **create**: Create new entities ``` create ExampleTree # Create single entity create 10 count of ExampleTree # Create multiple entities ``` - **force**: Relabel a value's units without conversion (no math applied). Prefer `as ` (without `force`), which routes through `start unit` conversions and catches mismatches. ``` force prior.temperature as degrees # Relabel only; the number is unchanged ``` ### Collection Operations ``` ExampleTree[ExampleTree.age > 5 years] # Filtering all # All entities of specified type ``` ### Control Flow **Event handlers can use conditional modifiers:** ``` attribute.event:if(condition) = expression attribute.event:elif(condition) = expression attribute.event:else = expression ``` **Ternary-style conditional expressions:** ``` value if condition else alternative ``` ### Assertions Assert statements validate conditions during execution: ``` assert condition assert condition otherwise message ``` Assertions verify entity state or simulation invariants and error with details if false. Disabled when `--suppress-errors` is set. ### Handlers Multi-statement blocks enclosed in curly braces `{}` supporting: - `const` variable declarations - `return` statements - Conditional blocks - Complex logic Example full body: ``` age.step = { const currentAge = prior.age const increment = 1 year return currentAge + increment } ``` Example lambda (single expression): ``` height.step = prior.height + sample uniform from 0 meters to 1 meters ``` ### Exports Exports define output files written during simulation and are the primary mechanism for evaluating model output. Export files are defined in the simulation stanza: ``` start simulation Main exportFiles.patch = "file:///tmp/patch_results_{replicate}.csv" end simulation ``` Multiple export targets supported. Export to CSV, NetCDF, or GeoTIFF via `--output-format`. Config variables usable in exportFiles paths via `{configName.variableName}` syntax. Example exporting attributes from patch stanza: ``` start patch Default ExampleTree.init = create 5 count of ExampleTree export.averageAge.step = mean(ExampleTree.age) export.averageHeight.step = mean(ExampleTree.height) end patch ``` The identifier after `export.` (e.g. `averageAge`) becomes the column / variable name in the output, and the expression assigned can be any valid josh expression. ### Debug Output Debug statements output values during execution for troubleshooting: ``` debug expression debug expression otherwise message ``` Outputs variable values and messages to debug stream. Disabled when `--suppress-errors` is set. ### State Management Entities can define custom states. Event handlers in a state are only evaluated if the state attribute on an entity is set to that given state. Example setting state on an organism: ``` start organism ExampleTree state.init = "alive" health.step = prior.health - 1 unit if state == "damaged" else prior.health state.step = "dead" if health < 1 unit else state end organism ``` Example of using state for handlers: ``` start state "dead" timeDead.step = prior.timeDead + 1 year end state ``` ### External Data External data references let a simulation read preprocessed `.jshd` files at each patch using the `external` keyword. See the **Geospatial Data Preprocessing** section and the `preprocess` command for how `.jshd` files are produced. `external ` is a pure lookup: each evaluation returns the value at (the calling handler's patch cell, the current step count). No stored state, no substep semantics — callable from any handler in any substep. **Bringing arbitrary units into Josh.** Unit names referenced from script code must be Josh identifiers (`[A-Za-z][A-Za-z0-9]*` — no spaces, dashes, or exponent notation). CF-1.8 strings like `"W m-2"` or `"kg m-2 s-1"` need an alias at preprocess time. A `start unit` block then declares the conversion to whatever working unit your model uses; `current` is the value being converted. Example preprocessing solar radiation (CF unit `W m-2`) as the alias `wm2`, convert to `mjday`, and read it directly from the organism: ```sh josh preprocess sim.josh Main rad.nc rsds wm2 solar.jshd ``` ``` start unit wm2 mjday = current * 0.0864 # W/m² instantaneous → MJ/m²/day daily integral end unit start patch Default Grass.init = create 5 count of Grass end patch start organism Grass height.init = 0 m height.step = prior.height + map external solar from [5 mjday, 25 mjday] to [0 m, 0.05 m] sigmoid end organism ``` The `wm2 → mjday` conversion fires automatically because `map`'s `from` clause expects `mjday`. The organism reads `external solar` directly. **Timestep access:** ``` external variableName # Current timestep external variableName at prior # Previous timestep external variableName at N # Timestep N (0-based) ``` **Patch-level mediation.** Introduce a patch attribute when the computation is genuinely patch-scale: combining multiple externals into a derived variable, applying shared threshold logic, sampling once per patch per step (e.g. a stochastic disturbance affecting all organisms on the patch), or aggregating across the patch's organisms. Organisms then read it via `here.`. ``` start patch Default Trees.init = create 5 of Tree groundLight.step = external solar * (1 count - mean(Trees.canopyShade)) end patch start organism Tree canopyShade.init = 0.1 count height.init = 0 m height.step = prior.height + map here.groundLight from [5 mjday, 25 mjday] to [0 m, 0.05 m] sigmoid end organism ``` For per-organism reads of raw external data (climate the organism responds to individually), read `external X` directly from the organism, no patch attribute needed. ### Other **Sampling:** ``` sample uniform from low to high sample count from collection sample count from collection with replacement sample count from collection without replacement ``` **Limiting:** ``` limit operand to [min,] # Minimum bound limit operand to [,max] # Maximum bound limit operand to [min,max] # Both bounds ``` **Mapping:** ``` map operand from [fromlow,fromhigh] to [tolow,tohigh] # Linear mapping (default) map operand from [fromlow,fromhigh] to [tolow,tohigh] linear # Linear mapping map operand from [fromlow,fromhigh] to [tolow,tohigh] sigmoid # Sigmoid mapping map operand from [fromlow,fromhigh] to [tolow,tohigh] quadratic # Quadratic mapping map operand from [fromlow,fromhigh] to [tolow,tohigh] method(arg) # Method with argument ``` Available mapping methods: - **linear**: Linear interpolation between ranges. Does not take optional arguments. - **sigmoid**: S-curve mapping for smooth transitions. Takes optional argument. True: increasing. False: decreasing. - **quadratic**: Quadratic curve mapping with controllable concavity. Takes optional argument. True: the center of the domain maps to range maximum, endpoints to minimum. False: the center maps to range minimum, endpoints to maximum. Examples: ``` map temperature from [0 C, 40 C] to [0 count, 100 count] quadratic(true) # Optimal temp in middle maps to max map temperature from [0 C, 40 C] to [0 count, 100 count] quadratic(false) # Inverse: edges map to max ``` **Spatial:** ``` identifier within distance radial at prior ``` ### Reserved Following reserved for future use and are not currently implemented: - Imports (`import`) - Named config (`config expression as identifier)` - Agent (`agent` and `organism` currently aliases) Following keywords are reserved and should not be used for user-defined entities or variables: `as`, `const`, `debug`, `disturbance`, `elif`, `else`, `end`, `if`, `management`, `limit`, `map`, `return`, `start`, `state`, `step`, `within`, `latitude`, `longitude` ## Other resources - [Additional documentation](https://www.joshsim.org/llms.txt) - [Latest Josh jar](https://www.joshsim.org/dist/main/joshsim-fat.jar)