---
title: "IoTSyn - Smart City & Public Spaces Edition"
subtitle: "Methodology and Normative Implementation Specification v4.4.0-RC1"
author: "IoTSyn Project"
date: "2026-07-04"
lang: en-US
---
```{=openxml}
```
# Contents
1. Purpose, scope, and non-goals
2. Design principles
3. Architecture and mandatory execution order
4. Units, numerical rules, and error policy
5. Randomness and core statistical engine
6. Environmental and behavioural generator
7. Thermal indices
8. Aggregation, hazard vector, and decisions
9. Output artifacts and data contract
10. Validation protocol
11. Scenario analysis
12. Edge-case contract
13. Reproducibility contract
14. Security, privacy, and ethical use
15. Scope and limitations
Appendix A. Known-answer and reference calculation vectors
Appendix B. Minimum normalized scenario structure
Appendix C. Implementation checklist
References
```{=openxml}
```
# Document status
**Version:** 4.4.0-RC1
**Status:** Release Candidate 1. The normative model content is complete; Final status is granted only after the reference implementation publishes the remaining conformance evidence (source commit, dependency lockfile, reference-container digest, golden artifacts, and a two-environment reproduction test), as required by Section 13.
**Primary purpose:** A normative, implementation-ready specification for developing, testing, and validating the updated IoTSyn Smart City & Public Spaces platform.
**Supersedes:** Specification v4.3.1.
This document is both a scientific methodology and a software conformance specification. It is written so that independent development teams can implement the same model without relying on undocumented choices.
The key words **MUST**, **MUST NOT**, **REQUIRED**, **SHALL**, **SHALL NOT**, **SHOULD**, **SHOULD NOT**, **RECOMMENDED**, **MAY**, and **OPTIONAL** are to be interpreted as normative requirements. Text explicitly labelled *Informative* explains rationale but does not define conformance.
# Revision summary
## Version 4.4.0-RC1
Version 4.4.0-RC1 responds to the external audit of v4.3.1. The mathematical content of every existing reference vector is unchanged and remains verified.
1. The ventilation monotonicity check is corrected: the sign of dC_inf/dk_vent equals the sign of C_bg(k_dep + k_chem) - E/V, so the validator now derives the expected direction per probe instead of assuming that more ventilation always lowers the steady state. Two-regime reference vectors are included.
2. Every stochastic algorithm is now pinned to normative pseudocode with an explicit random-consumption order: Box-Muller caching, Knuth and PTRS Poisson, Marsaglia-Tsang Gamma (including the alpha < 1 boost and scale handling), Beta as a ratio of two Gammas, the categorical rule, and the Normal CDF module used by the wind copula.
3. The solar module is renamed `iotsyn_solar_v1` and fully specified in Appendix D, with known-answer vectors.
4. All previously open initial conditions and edge cases are closed: Markov-chain start states, pollutant initial concentration, polar day/night UHI behaviour, audience-knot interpolation across midnight, zero-probability vectors, the uniform mapping used for arrival offsets, stable ordering of simultaneous events, the fate of deferred visitors after timeout, and the overall action level when every hazard is unavailable.
5. The data contract is externalized to machine-readable artifacts shipped with this specification: a complete CSV data dictionary, JSON Schemas for the JSON artifacts, and a normative tolerance map. Canonical JSON is pinned to RFC 8785; the arrival event log is pinned to JSON Lines with a defined record order.
6. The known-answer set is expanded from three groups to more than twenty, covering substream seed derivation, all distributions, the Normal CDF, an NHPP interval, solar geometry, saturation vapour pressure, the wind profile, energetic noise summation, largest-remainder allocation, partial and fully unavailable risk vectors, and the compound priority score.
7. Word rendering is hardened: interval notation in tables, bitwise operations, and unit superscripts are expressed as plain text or code so no normative requirement depends on equation rendering.
8. Document status is set to Release Candidate until the implementation-side conformance evidence exists.
## Version 4.3.1
Version 4.3.1 applies review corrections only. No numerical behaviour of a correctly implemented v4.3.0 component changes; the pipeline change makes an already-required data dependency explicit.
1. Thermal indices are computed at pipeline stage 7, before offered-arrival intensity construction, resolving the contradiction in which the thermal deterrence factor consumed a UTCI value that the mandatory order produced only at a later stage.
2. Section 6.9 is reworded accordingly; the alternative-thermal-predictor clause now applies only when UTCI is disabled.
3. `elevated_hazard_count` and `overall_action_level_provisional` are added to the mandatory CSV column contract, matching Section 8.5 and the reference test vectors.
4. A worked example clarifies the time-grid timestamp semantics.
5. The random-substream derivation salt is pinned as a versioned constant decoupled from the document version, so document revisions do not silently re-seed every stream.
6. Attribution corrections: the saturation vapour-pressure coefficients are attributed to the WMO-recommended Sonntag set rather than to Tetens (1930); the cloud transmissivity factor is attributed to Kasten and Czeplak (1980); the solar module and the Kolmogorov-Smirnov test receive citations; the Matsumoto and Nishimura title is completed.
7. The provenance of the NO2 operational bands relative to WHO 2021 values is stated precisely.
## Version 4.3.0
Version 4.3.0 resolves implementation ambiguities and mathematical defects identified in the v4.2.0 review. The principal changes are:
1. The platform is separated into four explicit layers: latent physical truth, sensor observation, aggregation/features, and risk/decision output.
2. MT19937 uniforms used by logarithms or inverse CDFs are mapped to the open interval \((0,1)\), preventing \(\log(0)\).
3. Named deterministic random substreams isolate components so adding a new channel does not perturb unrelated outputs.
4. The pollutant observation model is arithmetic-mean preserving by default.
5. Pollutant removal separates ventilation, deposition, and chemical loss.
6. Occupancy has explicit start conditions and optional hidden warm-up.
7. Offered, admitted, rejected, and deferred arrivals are distinct event classes; NHPP validation applies to offered arrivals.
8. Pedestrian sound energy is exactly zero when occupancy is zero, and \(L_{Aeq,T}\) is calculated through energy averaging over a declared window.
9. Audience segment is assigned to each arrival and remains fixed for the visitor's dwell period.
10. UTCI units, domain handling, categories, and operational bands are fully specified. WBGT follows a separate heat-only decision path.
11. Compound risk uses all elevated hazards, not a scalar sensitivity indexed by an undefined list-valued issue.
12. Decision objects declare whether they are nowcasts, forecasts, or ex-post planning outputs.
13. Reproducibility is defined as exact inside a pinned reference container and tolerance-based outside it.
14. Validation is divided into numerical correctness, statistical fidelity, internal consistency, and external benchmarking.
15. Output artifacts include a cryptographic manifest and schema/version metadata.
# 1. Purpose, scope, and non-goals
## 1.1 Purpose
IoTSyn generates transparent, reproducible synthetic urban public-space IoT datasets without training generative AI models. It combines explicit stochastic processes with simplified urban physics and a deterministic decision layer.
The specification supports research, software testing, education, scenario analysis, operations prototyping, and reproducible demonstrations. It is intended to produce internally coherent scenarios involving:
- outdoor meteorology and thermal exposure;
- PM2.5 and NO2 concentrations;
- environmental noise;
- pedestrian arrivals, occupancy, capacity, and audience composition;
- a four-hazard operational action vector;
- immediate risk communication; and
- episode-level behaviour-change planning objects.
## 1.2 Non-goals
IoTSyn does not claim to reproduce a specific city unless its parameters have been externally calibrated. It is not a computational-fluid-dynamics model, an atmospheric chemistry model, an individual pedestrian trajectory model, a medical diagnostic system, or a substitute for local emergency/public-health protocols.
No output from the rule-based decision layer SHALL be labelled a validated public-health recommendation unless a separate local governance and validation process has approved it.
## 1.3 Conformance profiles
An implementation MAY support one or both profiles:
- **Core profile:** all required generation, decision, validation, and serialization behaviour in this specification.
- **Extended profile:** optional sensor drift, finite-capacity deferral, WBGT, forecasts, external benchmarking, and additional pollutant species.
An implementation claiming v4.4.0 Core conformance MUST pass all mandatory known-answer, invariant, schema, and reference-scenario tests.
# 2. Design principles
**Transparent.** Every generated field has a declared source equation, unit, parameter set, random stream, and validation class.
**Layered.** Physical truth is never overwritten by sensor noise or decision logic.
**Deterministic.** A pinned reference runtime, canonical configuration, and seed reproduce exact artifacts.
**Extensible without random-sequence breakage.** Every component consumes a named random substream.
**Physically interpretable.** Simplifications are allowed, but they are explicit and dimensionally consistent.
**Decision-auditable.** Every message and strategy object is recomputable from stored data and rules.
**Honest about evidence.** Programmed couplings are reported as internal-consistency results, not empirical discoveries.
# 3. Architecture and mandatory execution order
## 3.1 Four data layers
| Layer | Meaning | Examples |
|---|---|---|
| Latent physical truth | Simulated environmental and behavioural state before measurement error | `pm25_physical_ug_m3`, true occupancy, offered arrivals |
| Sensor observation | Synthetic measurement process | `pm25_sensor_raw_ug_m3`, bias, drift, missingness |
| Aggregation and features | Windowed or derived variables | 24-hour rolling PM2.5, `LAeq_15min_dBA`, crowding ratio |
| Risk and decision | Operational classifications and guidance | hazard levels, overall action level, messages, episode strategies |
A value from a lower layer MUST remain available when a higher layer is generated from it.
## 3.2 Mandatory pipeline
The reference implementation SHALL execute the following stages in this order:
1. Load, canonicalize, and validate the scenario.
2. Initialize named random substreams.
3. Build the time grid, calendar, and solar geometry.
4. Generate cloud and precipitation state paths.
5. Generate meteorological truth channels.
6. Compute vapour pressure, relative humidity, and radiant-temperature inputs.
7. Calculate mean radiant temperature and thermal indices (UTCI and, when enabled, WBGT).
8. Generate the crowd-state path.
9. Build the piecewise-constant offered-arrival intensity.
10. Generate exact offered-arrival event times.
11. Apply admission/capacity policy.
12. Assign dwell time and persistent audience segment to admitted visitors.
13. Reconstruct occupancy and per-audience occupancy.
14. Generate traffic flow and species-specific emissions.
15. Integrate pollutant physical concentrations.
16. Generate pollutant sensor observations.
17. Generate source sound energies and rolling \(L_{Aeq,T}\).
18. Calculate complete-window rolling exposure features.
19. Calculate hazard components, overall action level, and per-step communication.
20. Construct risk episodes and strategy objects.
21. Run validation.
22. Serialize artifacts canonically and calculate hashes.
Stage 7 makes the thermal indices available to the arrival-deterrence factor of Section 6.9; the risk layer of Section 8 reuses the same stored per-interval values unchanged.
Changing this order constitutes an algorithm-version change and MUST update `algorithm_version`.
## 3.3 Time-grid semantics
Let the scenario begin at \(t_0\), contain \(n\) output rows, and have a fixed interval \(\Delta t\). Row \(j\) represents the half-open interval from \(t_j\) (inclusive) to \(t_{j+1}\) (exclusive), where
\[
t_j=t_0+j\,\Delta t.
\]
The CSV timestamp is the interval start. State variables are sampled at the interval end unless their field definition says otherwise. Interval totals, such as arrivals, count events in the half-open interval. Rolling windows are right-closed at the row's evaluation time and use exact duration weighting.
*Worked example (informative).* With \(t_0\) = 08:00:00 and \(\Delta t\) = 900 s, row 3 carries timestamp 08:45:00, represents the interval [08:45:00, 09:00:00), reports state variables sampled at 09:00:00, and counts arrival events that occurred inside that half-open interval.
Timestamps MUST use ISO 8601 with an explicit UTC offset. Internal calculations SHOULD use integer microseconds since the Unix epoch to avoid cumulative floating-point clock drift.
# 4. Units, numerical rules, and error policy
## 4.1 Canonical units
| Quantity | Canonical unit |
|---|---|
| Air and radiant temperature | degree Celsius |
| Temperature differences | kelvin |
| Wind speed | m/s |
| Pressure and vapour pressure in meteorology | Pa |
| Vapour pressure passed to UTCI polynomial | kPa |
| PM2.5 and NO2 concentration | microgram/m3 |
| Pollutant emission | microgram/s |
| Geometry | m, m2, m3 |
| Precipitation intensity | mm/h |
| Arrival intensity | persons/h |
| Dwell/exposure duration | s internally; min in human-facing summaries |
| Noise | dB(A), with declared averaging time |
All JSON configuration fields MUST carry units either in the field name or in adjacent metadata. Unit conversion MUST occur once, at a documented boundary.
## 4.2 Numeric domain policy
Every input field SHALL define one of these policies:
- `reject`: abort generation with a structured error;
- `clamp_and_flag`: clamp to a declared domain and emit a flag;
- `allow_with_warning`: retain the value and record a warning.
Scientific formula inputs MUST NOT be silently clamped.
## 4.3 Floating-point requirements
The reference runtime MUST be 64-bit and use IEEE 754 binary64 arithmetic. Non-finite values are forbidden in output artifacts. The serializer SHALL reject `NaN`, `+INF`, and `-INF`.
Exact cross-runtime equality is not claimed for transcendental functions. Section 13 defines the reproducibility contract.
# 5. Randomness and core statistical engine
## 5.1 Master seed and named substreams
`master_seed` SHALL be stored as a decimal string, not a native integer. Each component uses a deterministic 32-bit seed derived as follows:
```text
salt = "IoTSyn/v4.3.0" (stream_derivation_version 1)
material = UTF8(salt + "|" + master_seed + "|" + stream_name)
digest = SHA256(material) as 32 raw bytes
seed32 = unsigned big-endian integer represented by digest[0:4]
```
The salt is a pinned constant identified by `stream_derivation_version`. It changes only when the derivation scheme itself changes; it does NOT track the document version, so editorial revisions never silently re-seed every stream.
The Core profile defines these stream names:
```text
meteorology.cloud
meteorology.precipitation
meteorology.temperature
meteorology.humidity
meteorology.wind
crowd.state
crowd.arrivals
crowd.dwell
crowd.audience
traffic.flow
pollutant.pm25.source
pollutant.pm25.observation
pollutant.no2.source
pollutant.no2.observation
acoustics.residual
validation.bootstrap
```
A new component MUST receive a new stream name and MUST NOT consume values from an existing stream.
## 5.2 MT19937
The generator SHALL implement the original 32-bit MT19937 recurrence with a 624-word state. All intermediate state operations SHALL be reduced to unsigned 32 bits, and seeding SHALL use the Knuth recurrence (plain text is normative here; no equation rendering is required):
```text
u32(x) = x AND 0xFFFFFFFF
s[0] = u32(seed32)
s[i] = u32( 1812433253 * ( s[i-1] XOR (s[i-1] >> 30) ) + i ), i = 1..623
```
The twist and tempering operations SHALL match Matsumoto and Nishimura (1998). Implementations MUST pass the known-answer vectors in Appendix A.
## 5.3 Uniform conversion
Two mappings are defined over the raw 32-bit output y:
```text
U_half = y / 2^32 # in [0,1); zero is possible
U_open = (y + 0.5) / 2^32 # in (0,1); neither 0 nor 1 can occur
```
`U_open` is the default and is mandatory for logarithms, inverse CDFs, Box-Muller, exponential sampling, and any operation that requires a strictly open interval. `U_half` is used only where zero is explicitly permitted (categorical selection, arrival offsets). No inverse-CDF routine may consume a value equal to 0 or 1.
## 5.4 Normal distribution
For independent \(U_1,U_2\in(0,1)\):
\[
Z_0=\sqrt{-2\ln U_1}\cos(2\pi U_2),\qquad
Z_1=\sqrt{-2\ln U_1}\sin(2\pi U_2).
\]
The spare normal value MUST be cached inside the same named stream. It MUST NOT be shared across streams. The cache protocol is normative: if the stream cache is empty, draw \(U_1\) = U_open then \(U_2\) = U_open, return \(Z_0\), and store \(Z_1\); otherwise return the cached value and clear the cache. Every consumer of normal deviates on a stream (including Gamma sampling, Section 5.6) participates in the same cache.
## 5.5 Poisson distribution
For \(\lambda<30\), use Knuth's product algorithm with U_open draws:
```text
L = exp(-lambda); k = 0; p = 1
repeat: k = k + 1; p = p * U_open()
until p <= L
return k - 1
```
For \(\lambda\ge30\), use the PTRS transformed-rejection algorithm:
```text
b = 0.931 + 2.53*sqrt(lambda)
a = -0.059 + 0.02483*b
inv_alpha = 1.1239 + 1.1328/(b - 3.4)
v_r = 0.9277 - 3.6224/(b - 2)
repeat:
u = U_open() - 0.5
v = U_open()
u_s = 0.5 - abs(u)
k = floor((2*a/u_s + b)*u + lambda + 0.43)
if u_s >= 0.07 and v <= v_r: return k
if k < 0 or (u_s < 0.013 and v > u_s): continue
lhs = log(v*inv_alpha/(a/(u_s*u_s) + b))
rhs = -lambda + k*log(lambda) - lgamma(k + 1)
if lhs <= rhs: return k
```
A normal approximation is not conformant because its rounding and tail behaviour are implementation-dependent.
## 5.6 Other distributions
- Exponential: \(X=-\ln U/\lambda\) with one U_open draw.
- LogNormal: \(X=\exp(\mu+\sigma Z)\), with \(\mu,\sigma\) in log-space; consumes one normal deviate via Section 5.4.
- Weibull: \(X=c[-\ln(1-U)]^{1/k}\) with one U_open draw.
**Gamma (normative pseudocode, Marsaglia-Tsang).** The random-consumption order below is part of the contract; the normal draws participate in the stream's Box-Muller cache:
```text
gamma(alpha, scale, stream):
if alpha < 1:
g = gamma(alpha + 1, 1, stream) # boost first
u = U_open(stream) # then one uniform
return scale * g * u^(1/alpha)
d = alpha - 1/3; c = 1/sqrt(9*d)
loop:
repeat: x = normal(stream); v = (1 + c*x)^3 until v > 0
u = U_open(stream)
if u < 1 - 0.0331*x^4: return scale * d * v
if ln(u) < 0.5*x^2 + d*(1 - v + ln v): return scale * d * v
```
**Beta (normative).** \(Beta(a,b)\) SHALL be sampled as \(G_a/(G_a+G_b)\), where \(G_a\) = gamma(a, 1) is drawn first and \(G_b\) = gamma(b, 1) second, on the same stream.
**Normal CDF module `phi_normal_cdf_v1`.** The wind copula (Section 6.6) requires \(\Phi(z)\). The normative definition is \(\Phi(z)=\tfrac12\,\mathrm{erfc}(-z/\sqrt2)\), where erfc is the bundled double-precision module `erfc_v1`; its coefficients ship as a data file whose SHA-256 is recorded in the manifest, and it MUST pass the phi known-answer points in the reference vectors within an absolute tolerance of 5e-14. (PHP has no built-in erf, so the module is mandatory, not optional.)
- Categorical: choose the first category whose cumulative probability is strictly greater than one U_half draw; the final cumulative boundary is forced to exactly 1.
Distribution parameterizations MUST be echoed in `scenario.json`.
## 5.7 AR(1) convention
For a stationary standard-deviation parameter \(\sigma_x\):
\[
x_t=\mu_t+\phi(x_{t-1}-\mu_{t-1})+
\sigma_x\sqrt{1-\phi^2}\,Z_t.
\]
The specification always distinguishes stationary standard deviation from innovation standard deviation. Unless an explicit initial value is supplied, the process MUST be initialized from its stationary distribution or generated during a hidden warm-up.
# 6. Environmental and behavioural generator
**State-initialization contract (applies to every stochastic channel in this section).** Unless the scenario supplies explicit initial states, the pinned defaults at the start of warm-up are: cloud state `partly_cloudy`, precipitation `dry`, crowd state `normal`. AR(1) channels start from their stationary distribution (Section 5.7), sampled on their own stream at the first warm-up step. The pollutant initial concentration uses `initial_concentration_mode`; the default `steady_state_first_interval` sets C(start of warm-up) to the C_infinity implied by the first warm-up interval's inputs. When any pinned default chain state is used, `warmup_duration_minutes` MUST be at least 24 hours in addition to the rolling-window requirement of Section 6.11, so the visible horizon does not start from the arbitrary default. Scenario-supplied `initial_states` override all defaults and are echoed in the normalized scenario.
## 6.1 Calendar and solar geometry
A scenario MUST record `latitude_deg`, `longitude_deg`, `timezone`, `start_timestamp`, and `solar_position_algorithm_version`. The implementation MUST use one versioned solar-position module consistently and publish known-answer tests for solar elevation and azimuth.
The Core profile bundles `iotsyn_solar_v1`, a pinned implementation of the NOAA solar-position approximation equations (cf. Meeus, 1998). Its complete normative equations are given in Appendix D and its known-answer vectors ship in the reference-vector file. A different module is allowed only if its version and test vectors are stored and the algorithm version changes.
## 6.2 Cloud fraction
Cloud condition is a three-state, stepwise Markov chain: `clear`, `partly_cloudy`, `overcast`. The default transition matrix is:
| From / to | clear | partly_cloudy | overcast |
|---|---:|---:|---:|
| clear | 0.85 | 0.14 | 0.01 |
| partly_cloudy | 0.15 | 0.75 | 0.10 |
| overcast | 0.03 | 0.17 | 0.80 |
Within each state, `cloud_fraction` is sampled from a Beta distribution:
| State | alpha | beta |
|---|---:|---:|
| clear | 2 | 8 |
| partly_cloudy | 4 | 4 |
| overcast | 8 | 2 |
The values are synthetic defaults, not universal climatology. A calibrated scenario SHOULD replace them.
## 6.3 Precipitation
Rain state is a two-state Markov chain. The probability of entering and remaining wet depends on cloud state:
| Cloud state | P(wet at t given dry at t-1) | P(wet at t given wet at t-1) |
|---|---:|---:|
| clear | 0.005 | 0.20 |
| partly_cloudy | 0.03 | 0.55 |
| overcast | 0.15 | 0.80 |
When wet, intensity is Gamma distributed in mm/h with configurable shape and scale; defaults are shape 1.5 and scale 1.5. When dry, precipitation is exactly zero.
## 6.4 Air temperature and urban heat island
Air temperature is:
\[
T_a(t)=T_{mean}+A_T\cos\left(\frac{2\pi(h-\phi_T)}{24}\right)
+\Delta T_{UHI}(t)+\epsilon_T(t).
\]
Let `sunset_time` be derived from the solar module, and let
\[
h_{peak}=sunset\_time+uhi\_peak\_lag\_hours.
\]
Define the 24-hour circular distance \(d_{24}(h,h_{peak})\). The nocturnal shape is
\[
g(h)=I(\alpha_{solar}<0)\exp\left[-\frac12
\left(\frac{d_{24}(h,h_{peak})}{\sigma_{UHI,h}}\right)^2\right].
\]
Then
\[
\Delta T_{UHI}=\Delta T_{max}\,g(h)
(1-c_{cloud}\,cloud\_fraction)
\left(1-w_u\frac{\min(v_{10},v_{sat})}{v_{sat}}\right).
\]
The final wind factor MUST be clamped to \([0,1]\). All coefficient domains are validated before generation.
**Polar and no-sunset handling.** If the solar module reports no sunset for the scenario date (polar day), the nocturnal indicator is zero everywhere and the UHI term is zero. If it reports no sunrise (polar night), \(h_{peak}\) is defined as local solar midnight plus `uhi_peak_lag_hours` and the indicator is one for all hours. Both cases are flagged in validation metadata.
## 6.5 Vapour pressure and relative humidity
Saturation vapour pressure in Pa is calculated with a Magnus-type formula. The coefficients below are the WMO-recommended set (Sonntag, 1990; WMO, 2018); they differ from the historical Tetens (1930) coefficients, so the popular label "Magnus-Tetens" is retained only informally:
\[
e_{sat}(T)=611.2\exp\left(\frac{17.62T}{243.12+T}\right).
\]
Latent log vapour pressure is an AR(1) process around a configurable diurnal mean:
\[
x_e(t)=\log e_{mean}(t)+\phi_e[x_e(t-1)-\log e_{mean}(t-1)]
+\sigma_e\sqrt{1-\phi_e^2}Z_t.
\]
\[
e_{raw}(t)=\exp(x_e(t)),\qquad
e(t)=\min(e_{raw}(t),e_{sat}(T_a(t))).
\]
\[
RH(t)=100\,e(t)/e_{sat}(T_a(t)).
\]
The physical column `vapour_pressure_pa` is emitted. `relative_humidity_pct` is clamped only for floating-point roundoff to \([0,100]\); substantive supersaturation is prevented by the vapour-pressure cap and is counted in validation.
## 6.6 Wind with a Weibull marginal and temporal dependence
The Core profile uses a Gaussian-copula AR(1) process:
\[
z_t=\phi_w z_{t-1}+\sqrt{1-\phi_w^2}\epsilon_t,\qquad \epsilon_t\sim N(0,1),
\]
\[
q_t=\Phi(z_t),\qquad
v_{10,t}=c[-\ln(1-q_t)]^{1/k}.
\]
This preserves a Weibull marginal while introducing temporal dependence. Validation tests the transformed latent values or the marginal CDF, not an incorrect i.i.d. assumption on the final time series.
Pedestrian-height wind is:
\[
v(z)=v_{10}\frac{\ln(z/z_0)}{\ln(10/z_0)},
\]
valid only for \(z>z_0\). The default pedestrian height is 1.5 m. Invalid roughness configurations are rejected.
## 6.7 Mean radiant temperature approximation
The Core profile uses a declared operational approximation, not a full radiation model. Define
\[
S(t)=\max(0,\sin\alpha_{solar})
\left(1-0.75\,cloud\_fraction^{3.4}\right),
\]
where the cloud transmissivity factor follows Kasten and Czeplak (1980),
and
\[
T_{mrt}=T_a+k_{solar}(1-shade\_fraction)
(1+0.5\,surface\_albedo)S(t)-k_{night}I(\alpha_{solar}<0)(1-cloud\_fraction).
\]
Defaults are `k_solar_K = 25` and `k_night_K = 3`. The approximation and its limitations MUST be stated in output metadata.
## 6.8 Crowd-state process
Crowd state is a discrete-time Markov chain over `quiet`, `normal`, `busy`, and `event`. Transition matrices MAY vary by hour, day type, or scheduled event. All matrices MUST be row-stochastic within a tolerance of \(10^{-12}\).
The full state path is generated before arrivals. Scheduled events MAY force the state or modify transition probabilities; the mechanism MUST be identified as `forced` or `stochastic` per interval.
## 6.9 Offered-arrival intensity
The conditional intensity is piecewise constant on each simulation interval:
\[
\lambda_j=\lambda_0\,d_j\,s_{day,j}\,m_{state,j}\,W_j.
\]
The weather factor is
\[
W_j=W_{thermal,j}W_{rain,j}W_{wind,j},
\]
\[
W_{thermal}=\exp\left[-\left(\frac{UTCI-UTCI_{neutral}}{\sigma_c}\right)^2\right],
\]
\[
W_{rain}=\exp(-\beta p),\qquad
W_{wind}=\exp[-\gamma\max(0,v_{ped}-v_0)].
\]
All factors MUST lie in the half-open interval (0, 1], zero excluded and one included. UTCI is available at this stage because thermal indices are computed at pipeline stage 7, before intensity construction (Section 3.2); the deterrence term consumes exactly those stored per-interval values, and the risk layer later reuses them unchanged. If a scenario disables UTCI, it MUST declare an alternative thermal predictor for \(W_{thermal}\); no hidden fallback is allowed.
### Exact event generation
Because \(\lambda_j\) is constant within interval \(j\), the reference method is:
1. \(K_j\sim Poisson(\lambda_j\Delta t_{hours})\) on stream `crowd.arrivals`.
2. Generate \(K_j\) independent offsets, each U_half times \(\Delta t\) (zero is allowed: an event exactly at the interval start belongs to that interval by Section 3.3).
3. Sort offsets ascending with a stable sort; equal offsets keep generation order, and each event receives a monotonically increasing `event_sequence_id`.
4. Add them to \(t_j\).
This is exact for the specified piecewise-constant conditional NHPP and is preferred to global thinning.
## 6.10 Capacity and admission
The scenario declares `space_capacity_persons = K` and one of:
- `report_only`: all offered arrivals are admitted; capacity is a reporting threshold.
- `reject`: an offered arrival is rejected if active admitted occupancy is already \(K\).
- `defer_fifo`: a blocked arrival enters a FIFO queue until a place opens or `maximum_defer_minutes` is exceeded.
Events at an identical timestamp are processed in this order: departures, deferred admissions, new offered arrivals; within the same class, events are processed in ascending `event_sequence_id` (stable, deterministic). Exact ties generated by continuous uniforms are rare, but the rule is mandatory.
A deferred visitor whose waiting time reaches `maximum_defer_minutes` leaves the queue at exactly arrival time plus that limit, is logged as `deferred_timeout`, and is counted in `deferred_timeout_count`. The cumulative conservation identity offered = admitted + rejected + deferred_timeout + current queue length is a mandatory deterministic check.
The event log distinguishes:
- `offered_arrival`;
- `admitted_arrival`;
- `rejected_arrival`;
- `deferred_arrival`;
- `deferred_admission`;
- `deferred_timeout`;
- `departure`.
NHPP time-rescaling validation applies to offered arrivals. It MUST NOT be applied to capacity-filtered admissions.
## 6.11 Dwell times and occupancy initial conditions
Each admitted arrival receives an independent LogNormal dwell time. Let \(t_0\) be the output horizon start. The conditional expectation is
\[
E[N(t)\mid\lambda]=E[N_{0,survivors}(t)]
+\int_{t_0}^{t}\lambda(s)\bar G(t-s)\,ds.
\]
The scenario MUST use one initial mode:
- `empty`: no active visitors at \(t_0\);
- `explicit`: initial visitors and residual dwell times are supplied;
- `sampled_stationary`: a hidden warm-up is run and active visitors crossing \(t_0\) are retained.
`warmup_duration_minutes` MUST be at least the longest rolling window and SHOULD also exceed a high dwell-time quantile when stationary initialization is requested.
Occupancy is reconstructed from event intervals, not from a steady-state approximation.
## 6.12 Persistent audience assignment
At admission time \(t_i\), visitor \(i\) is assigned
\[
A_i\sim Categorical(p(t_i,space,day,state)).
\]
The category remains fixed until departure. Per-audience occupancy is the number of active visitors carrying each category.
The Core categories are:
```text
general_public
families
elderly
students
commuters
event_attendees
```
These are mutually exclusive *primary communication segments* by construction, not claims about mutually exclusive real-world identities.
The probability table is supplied as hourly/context knots and linearly interpolated between adjacent hourly knots before normalization; the knot axis is circular over the 24-hour clock, so interpolation between the 23:00 and 00:00 knots crosses midnight continuously. Negative probabilities are rejected, and a vector whose interpolated sum is zero at any evaluation time is a configuration error rejected at canonicalization. A deterministic largest-remainder aggregate mode MAY be supported for backward compatibility, but it MUST be labelled `audience_assignment_mode = aggregate_largest_remainder` and is not the default.
## 6.13 Traffic flow
Traffic is independent of pedestrian occupancy except through shared calendar drivers. The default model is
\[
q_t=q_{base}d_{traffic}(t)s_{day}(t)\exp(x_t-\tfrac12\sigma_q^2),
\]
where \(x_t\) is a zero-mean stationary AR(1) process with variance \(\sigma_q^2\). The exponential correction preserves the arithmetic mean.
`traffic_flow_vehicles_h` is non-negative and MAY be rounded to an integer count only if the scenario explicitly requests a count process. Otherwise it remains a continuous rate.
## 6.14 Pollutant emissions and physical box model
For pollutant species \(r\), traffic emissions are
\[
E_r(t)=\frac{q(t)\,EF_r}{3600}+E_{activity,r}(t),
\]
where `EF_r` is in microgram per vehicle and \(E_r\) is in microgram/s. `EF_r` is the average emission mass attributed to one vehicle while it is inside the box footprint; it is a scenario-level calibration parameter, not a per-kilometre emission factor.
Let \(V=HA\), \(k_{vent}=u_{eff}/L_x\), and let \(k_{dep}\) and \(k_{chem}\) be species-specific loss rates in \(s^{-1}\):
\[
\frac{dC}{dt}=\frac{E}{V}+k_{vent}C_{bg}
-(k_{vent}+k_{dep}+k_{chem})C.
\]
For constant inputs during a step:
\[
k_{tot}=k_{vent}+k_{dep}+k_{chem},
\]
\[
C_{\infty}=\frac{E/V+k_{vent}C_{bg}}{k_{tot}},
\]
\[
C(t+\Delta t)=C_{\infty}+[C(t)-C_{\infty}]e^{-k_{tot}\Delta t}.
\]
If \(k_{tot}=0\), the exact linear update \(C(t+\Delta t)=C(t)+E\Delta t/V\) is used. Negative concentrations are numerical errors and cause validation failure.
### Calm-wind regularization
The active mode is recorded:
- `additive_baseline`: \(u_{eff}=u+u_{min}\);
- `hard_floor`: \(u_{eff}=\max(u,u_{min})\).
`u_min` represents residual ventilation only. It MUST NOT be described as deposition. Deposition is represented by \(k_{dep}\).
## 6.15 Pollutant sensor observation
The default arithmetic-mean-preserving multiplicative observation model is
\[
C_{sensor,raw}=C_{physical}
\exp(\eta-\tfrac12\sigma_{obs}^2),
\qquad \eta\sim N(0,\sigma_{obs}^2).
\]
Therefore
\[
E[C_{sensor,raw}\mid C_{physical}]=C_{physical}.
\]
An optional extended model is
\[
\log C_{sensor,raw}=\log C_{physical}+b_{sensor}+d_t+\epsilon_t-\tfrac12\sigma_\epsilon^2,
\]
\[
d_t=\rho_d d_{t-1}+\nu_t.
\]
Bias, drift, calibration, missingness, quantization, and saturation are observation-layer properties and MUST NOT modify `C_physical`.
When \(C_{physical}=0\), the default multiplicative observation is exactly zero. Such rows are excluded from log-ratio diagnostics and counted separately.
## 6.16 Environmental noise
All sound combination is performed in energy space. Let the reference intensity be arbitrary but consistent, \(I_0=1\). For a level \(L\):
\[
I(L)=10^{L/10}.
\]
Pedestrian source energy is
\[
I_{ped}(N)=\begin{cases}
0,&N=0,\\
I_{ped,ref}N/N_{ref},&N>0.
\end{cases}
\]
Traffic source energy is
\[
I_{traffic}(q)=\begin{cases}
0,&q=0,\\
I_{traffic,ref}q/q_{ref},&q>0.
\end{cases}
\]
The instantaneous total energy is
\[
I_{total}=I_{bg}+I_{traffic}+I_{ped}.
\]
An AR(1) residual in dB may be applied multiplicatively in energy:
\[
I_{noisy}=I_{total}10^{\epsilon_{dB}/10}.
\]
For assessment window \(T\):
\[
L_{Aeq,T}=10\log_{10}\left(
\frac{1}{T}\int_{t-T}^{t} I_{noisy}(s)\,ds
\right).
\]
For the window integral, \(I_{noisy}\) is evaluated from the row state (sampled per Section 3.3) and treated as constant across that row interval. The implementation SHALL use exact overlap weighting when the window is not an integer multiple of the simulation step. A result is `unavailable` until a complete window exists, unless hidden warm-up supplies the missing history.
# 7. Thermal indices
## 7.1 UTCI input contract
Thermal indices are computed once per interval at pipeline stage 7 (Section 3.2) and consumed unchanged by both the arrival-deterrence factor (Section 6.9) and the risk layer (Section 8). The primary outdoor index is UTCI. Inputs are:
- \(T_a\), degree Celsius;
- \(T_{mrt}\), degree Celsius;
- 10 m wind \(v_{10}\), m/s;
- vapour pressure passed to the polynomial in kPa.
The required pressure conversion is
\[
p_a[\mathrm{kPa}]=e[\mathrm{Pa}]/1000.
\]
The operational polynomial module MUST be versioned and must pass published test vectors. The polynomial has more than two hundred coefficients and is deliberately not reproduced in this document: the implementation SHALL bundle the canonical coefficient table as a data file whose SHA-256 is recorded in the manifest, and SHALL generate `utci_kat.json` from that bundled canonical implementation at build time; Section 10.3 verifies both. The accepted domain for `utci_approx_v1` is:
| Input | Domain |
|---|---|
| T_a | -50 to +50 degree Celsius |
| v_10 | 0.5 to 17 m/s |
| T_mrt minus T_a | -30 to +70 K |
| p_a | 0 to 5 kPa |
The default policy is `clamp_and_flag`. Original and clamped inputs MUST both be recoverable in validation metadata, and the CSV emits `utci_input_clamped`.
## 7.2 UTCI scientific categories
Intervals are half-open, eliminating boundary ambiguity:
| UTCI (degree Celsius) | Scientific category |
|---|---|
| below -40 | extreme cold stress |
| -40 to below -27 | very strong cold stress |
| -27 to below -13 | strong cold stress |
| -13 to below 0 | moderate cold stress |
| 0 to below 9 | slight cold stress |
| 9 to below 26 | no thermal stress |
| 26 to below 32 | moderate heat stress |
| 32 to below 38 | strong heat stress |
| 38 to below 46 | very strong heat stress |
| 46 and above | extreme heat stress |
Every bound is lower-inclusive and upper-exclusive.
## 7.3 UTCI operational level
The IoTSyn operational mapping is intentionally coarser and is not described as symmetric:
| UTCI interval (lower-inclusive) | `thermal_risk_level` |
|---|---|
| 9 to below 26 | low |
| 0 to below 9, or 26 to below 32 | caution |
| -13 to below 0, or 32 to below 38 | high_risk |
| below -13, or 38 and above | severe |
The scientific category and operational level are stored separately.
## 7.4 WBGT optional path
WBGT is a heat-stress screening index and SHALL NOT reuse the year-round UTCI classification table. If enabled, the output is `wbgt_outdoor_c` and a separate `wbgt_heat_action_level` derived from an explicitly named policy.
A WBGT policy MUST record:
- metabolic-rate class;
- clothing adjustment;
- acclimatization assumption;
- exposure duration or work/rest schedule;
- whether solar load is present;
- policy source and version.
WBGT output is unavailable for cold-risk classification. If UTCI is disabled, the thermal component outside a valid WBGT heat context is marked `unavailable`, not `low`.
# 8. Aggregation, hazard vector, and decisions
## 8.1 Rolling-window rule
All rolling exposure metrics use a declared duration and exact time weighting. The default policy is `require_complete_window`. A value is unavailable until the full period is covered by generated or warm-up history.
A partial-window value MAY be emitted for diagnostics as a separately named field ending in `_partial`; it MUST NOT drive risk unless `allow_partial_risk = true` is explicitly set.
## 8.2 Air-quality operational profile
IoTSyn defaults are operational simulation bands inspired by the WHO 2021 guideline and interim-target structure. They are not direct clinical alert thresholds and MUST be identified as `iotsyn_operational_24h_v1`.
Rolling air-quality features consume the calibrated sensor column when calibration is enabled and the raw sensor column otherwise; the resolved choice is echoed as `air_quality_feature_source` in the normalized scenario. The required averaging period is 24 hours:
| Species, microgram/m3 | low | caution | high_risk | severe |
|---|---:|---:|---:|---:|
| PM2.5 rolling 24 h | <15 | 15 to <25 | 25 to <50 | >=50 |
| NO2 rolling 24 h | <25 | 25 to <50 | 50 to <100 | >=100 |
*Band provenance (informative).* The PM2.5 boundaries coincide with the WHO 2021 24-hour guideline value (15) and interim targets 4 (25) and 2 (50). For NO2, only the 25 microgram/m3 boundary is a WHO 2021 24-hour guideline value; the 50 and 100 boundaries are IoTSyn operational subdivisions, since the sole WHO 24-hour interim target for NO2 is 120.
The air-quality component is the worse available species level. If neither complete species window is available, the component is `unavailable`.
A jurisdiction-specific profile SHOULD replace these defaults for operational deployment.
## 8.3 Noise operational profile
The default context-neutral simulation bands for the declared \(L_{Aeq,T}\) are:
| dB(A) | Level |
|---|---|
| <55 | low |
| 55 to <65 | caution |
| 65 to <75 | high_risk |
| >=75 | severe |
These are configurable operational bands, not universal ISO health limits. The assessment window and context are mandatory metadata.
## 8.4 Crowding component
The primary capacity metric is
\[
operational\_capacity\_ratio=N/K.
\]
The default bands are:
| Ratio | Level |
|---|---|
| <0.5 | low |
| 0.5 to <0.8 | caution |
| 0.8 to <1.0 | high_risk |
| >=1.0 | severe |
When usable area is known, the platform SHOULD also emit `density_persons_m2`. Capacity ratio is an operational measure, not a complete crowd-safety model.
## 8.5 Risk vector and overall action level
Ordinal numeric encoding is:
```text
low = 0
caution = 1
high_risk = 2
severe = 3
unavailable = null
```
The per-step object is conceptually:
```json
{
"thermal": 3,
"air_quality": 2,
"noise": 0,
"crowding": 2
}
```
Derived fields are:
- `overall_action_level`: maximum available component, using `conservative_max_v1`;
- `dominant_issue`: first maximum according to a documented precedence;
- `co_dominant_issues`: all hazards tied at the maximum;
- `elevated_issues`: all hazards at `high_risk` or `severe`;
- `elevated_hazard_count`: length of `elevated_issues`;
- `compound_risk_flag`: true when at least two hazards are elevated;
- `risk_completeness_fraction`: available enabled hazards divided by enabled hazards;
- `overall_action_level_provisional`: true when completeness is below 1.
The old name `combined_public_space_risk_level` MAY be emitted as a deprecated alias for backward compatibility, but new code SHALL use `overall_action_level`.
If every enabled hazard is `unavailable`, then `overall_action_level` is `unavailable` (serialized as null), `risk_completeness_fraction` is 0, `overall_action_level_provisional` is true, and the communication layer emits the `insufficient_data` template. `elevated_hazard_count` counts elevated hazards among the available components and is 0 in that case.
The maximum is an operational action aggregation rule. It MUST NOT be presented as a quantitatively calibrated combined health risk.
## 8.6 Immediate risk communication
A per-step message is generated from the overall level, elevated hazards, and available audiences. The output MUST store a message-template identifier and all trigger values.
Air-quality messages distinguish immediate exposure reduction from long-term mobility policy. During an elevated episode, baseline guidance is to alter time, route, or activity intensity and move away from traffic exposure; it SHALL NOT simply promote more exertion in polluted conditions.
## 8.7 Decision timing modes
Every decision object declares:
- `decision_mode`: `nowcast`, `forecast`, or `ex_post`;
- `issued_at`;
- `valid_from` and `valid_to`;
- `forecast_lead_minutes`, when mode is `forecast`;
- `forecast_model_version`, when mode is `forecast`.
A strategy reconstructed from realized future data MUST use `ex_post`. It cannot claim advance notice unless a forecast layer generated the trigger using only information available at issuance time.
## 8.8 Risk episodes
An episode is formed separately for each hazard-set condition. The algorithm is:
1. Mark intervals whose overall action level is at least the configured `episode_minimum_level`.
2. Form maximal contiguous runs with the same elevated-hazard set and same overall level.
3. Drop runs shorter than `minimum_episode_duration_minutes`.
4. Merge adjacent qualifying runs separated by no more than `episode_merge_gap_minutes` if their elevated-hazard sets are identical; the merged peak is the worse level.
5. Select audiences whose peak count is at least `salience_threshold_persons`.
Each episode stores exact start/end, duration, peak level, elevated hazards, peak audience count, and audience person-minutes. An episode truncated by the visible-horizon boundary is marked `boundary_censored: true`. The exact JSON field names of the episode object are pinned by the artifact-schema file of Section 9.5.
Person-minutes are calculated by integrating active segment counts over exact interval overlap, not by multiplying a single peak count by duration.
## 8.9 Audience-hazard sensitivity
The default matrix is configurable:
```json
{
"general_public": {"thermal":1.0,"air_quality":1.0,"noise":1.0,"crowding":1.0},
"families": {"thermal":1.2,"air_quality":1.2,"noise":1.1,"crowding":1.2},
"elderly": {"thermal":1.4,"air_quality":1.4,"noise":1.1,"crowding":1.2},
"students": {"thermal":1.0,"air_quality":1.0,"noise":1.0,"crowding":1.0},
"commuters": {"thermal":1.0,"air_quality":1.1,"noise":1.0,"crowding":1.1},
"event_attendees": {"thermal":1.1,"air_quality":1.0,"noise":1.0,"crowding":1.3}
}
```
These values are design assumptions, not individual medical attributes.
## 8.10 Compound priority score
Let \(R\) be the episode's elevated hazards. Default risk weights are `high_risk = 1` and `severe = 2`. Define
\[
H(a)=\min\left(H_{cap},\sum_{r\in R}w(level_r)sensitivity(a,r)\right),
\]
\[
X(a)=\log_{10}\left(1+\frac{audience\_person\_minutes}{E_{ref}}\right),
\]
\[
priority(a)=H(a)X(a).
\]
Defaults are \(H_{cap}=6\) and \(E_{ref}=1000\) person-minutes. Every component of the score is stored. A different formula requires a new `priority_algorithm_version`.
## 8.11 Strategy provenance
Each strategy object distinguishes data-derived and template-derived fields. Required fields include:
```json
{
"trigger_rule": "...",
"assumption_source": "...",
"evidence_basis": {
"episode": "generated_data",
"exposure": "generated_data",
"barrier": "design_template",
"offered_benefit": "design_template"
},
"confidence_level": "template_default"
}
```
No behavioural barrier or motivation may be represented as sensor-derived unless an external data source actually supplied it.
# 9. Output artifacts and data contract
Every run emits a common basename:
```text
.csv
.scenario.json
.arrival_events.json
.strategy.json
.validation.json
.manifest.json
```
## 9.1 CSV column groups
The CSV MUST contain at least:
### Time and context
- `timestamp`
- `step_duration_seconds`
- `day_type`
- `cloud_state`
- `cloud_fraction`
- `precipitation_mm_h`
### Meteorological truth
- `air_temperature_c`
- `vapour_pressure_pa`
- `relative_humidity_pct`
- `wind_speed_10m_m_s`
- `wind_speed_pedestrian_m_s`
- `mean_radiant_temperature_c`
### Mobility and occupancy
- `crowd_state`
- `offered_arrivals_count`
- `admitted_arrivals_count`
- `rejected_arrivals_count`
- `deferred_arrivals_count`
- `deferred_timeout_count`
- `queue_length_persons` (zero unless `capacity_mode` is `defer_fifo`)
- `crowd_state_mechanism` (`stochastic` or `forced`, Section 6.8)
- `occupancy_persons`
- `operational_capacity_ratio`
- six `audience_*_persons` columns
- `dominant_audience`
### Traffic and pollutants
- `traffic_flow_vehicles_h`
- per species: emission, background, physical concentration, sensor raw concentration, calibrated concentration if enabled, complete rolling concentration, and rolling availability flag
### Acoustics
- `LAeq_T_dBA`, where the exact column name or adjacent metadata identifies \(T\)
- `noise_window_complete`
### Thermal and risk
- `utci_c`
- `utci_stress_category`
- `utci_input_clamped`
- optional `wbgt_outdoor_c`
- four component levels
- `overall_action_level`
- `dominant_issue`
- `co_dominant_issues`
- `elevated_issues`
- `elevated_hazard_count`
- `compound_risk_flag`
- `overall_action_level_provisional`
- `risk_completeness_fraction`
- `risk_communication_template_id`
- `risk_communication_message`
Array-like CSV fields use canonical JSON arrays with no spaces.
## 9.2 Scenario file
`scenario.json` is the complete normalized configuration echo. It MUST include:
- schema, algorithm, and serialization versions;
- master seed and stream derivation version;
- runtime and reference-container metadata;
- all distribution and physical parameters;
- initial-condition and warm-up policy;
- all window durations and incomplete-window policy;
- thresholds, precedence, message templates, and strategy templates;
- sensitivity matrix and priority formula parameters;
- units and field precision map.
No undocumented default is allowed. Defaults are materialized into the normalized file.
## 9.3 Arrival event file
The file format is JSON Lines: exactly one canonical JSON object (RFC 8785) per line, records ordered by (timestamp, event-class order of Section 6.10, event_sequence_id). Each record has a unique deterministic event ID, `event_sequence_id`, exact timestamp, event type, visitor ID where applicable, audience segment, offered/admission status, dwell time, scheduled departure, and relevant crowd state.
If the event file is suppressed for size, the manifest records `arrival_event_artifact = omitted`, but the validator MUST consume the complete in-memory stream before disposal. Exact regeneration still requires the same scenario and seed.
## 9.4 Manifest
The manifest includes byte size and SHA-256 for every artifact, plus:
```json
{
"schema_version": "4.4.0",
"algorithm_version": "iotsyn-smartcity-4.4.0",
"serialization_profile": "canonical-json-csv-v1",
"git_commit": "...",
"runtime": "...",
"container_digest": "..."
}
```
## 9.5 Machine-readable contract artifacts
The following artifacts ship with this specification and are normative. The validator MUST load them instead of re-encoding the contract in code:
- `IoTSyn-v4_4_0-csv-data-dictionary.json`: every CSV column with name, type, unit, allowed values, nullability, and decimal precision. CSV serialization follows RFC 4180; null is the empty field; booleans are lowercase true/false; array-valued fields hold a canonical JSON array inside one quoted field.
- `IoTSyn-v4_4_0-artifact-schemas.json`: JSON Schemas (draft 2020-12) for the manifest, the normalized scenario skeleton, the strategy file, the validation report, and the arrival-event record.
- `IoTSyn-v4_4_0-tolerance-map.json`: the normative per-field-class numeric tolerances of Section 13.2.
- `IoTSyn-v4_4_0-reference-test-vectors.json`: the complete known-answer set; Appendix A reproduces only a subset.
Canonical JSON throughout this specification means the JSON Canonicalization Scheme, RFC 8785.
# 10. Validation protocol
## 10.1 Validation classes
Every check declares one class:
- `numerical_correctness`: exact/invariant or tolerance-based mathematical checks;
- `statistical_fidelity`: checks of stochastic generators;
- `internal_consistency`: checks that programmed relationships appeared;
- `external_benchmark`: comparisons with real external data.
Internal consistency MUST NOT be described as empirical validation.
## 10.2 Status semantics
- `FAIL`: deterministic contract or physical invariant violated.
- `WARN`: statistical test rejection, insufficient sample size, domain clamping, incomplete component, or plausibility concern.
- `PASS`: check executed and criterion met.
- `NOT_EVALUATED`: prerequisites not satisfied.
A stochastic goodness-of-fit rejection alone is a warning, because a correct generator has a non-zero false-rejection probability.
## 10.3 Deterministic checks
Mandatory hard checks include:
- schema validity;
- no non-finite numbers;
- time-grid continuity;
- range and unit checks;
- audience counts sum exactly to occupancy;
- occupancy recomputes from events;
- capacity-policy event accounting;
- pollutant physical series recomputes within tolerance;
- observed pollutant columns do not alter physical columns;
- rolling windows use correct history and duration;
- thermal and risk columns recompute exactly from normalized rules;
- strategy episode bounds match source intervals;
- manifest hashes match artifacts.
## 10.4 Distributional tests
The one-sample KS test (Kolmogorov, 1933) is applied only where the reference distribution is fully specified by configuration, not estimated from the tested sample.
| Component | Tested quantity | Reference |
|---|---|---|
| Normal generator | raw test draws or AR innovations | configured Normal |
| Pollutant observation | corrected log ratio | configured Normal |
| Dwell | raw independent samples | configured LogNormal |
| Wind | Weibull marginal transform or PIT | Uniform/Weibull |
| Time-rescaled arrivals | integrated-intensity increments | Exp(1) |
A KS test requires at least 50 usable observations by default. Below that, the result is `NOT_EVALUATED`.
When multiple tests are run as one validation family, Holm-Bonferroni adjustment is applied at family-wise alpha 0.05. Raw and adjusted p-values are both reported.
## 10.5 Time-rescaling test
For offered events \(t_i\), with piecewise-constant conditional intensity:
\[
z_i=\Lambda(t_i)-\Lambda(t_{i-1}),\qquad
\Lambda(t)=\int_{t_0}^{t}\lambda(s\mid state\ path)\,ds.
\]
The \(z_i\) should be i.i.d. Exp(1). The interval from \(t_0\) to the first offered event is included; the right-censored interval after the last event is not treated as an event interval.
## 10.6 Pollutant checks
- Reintegrate every physical step using stored inputs.
- Verify non-negativity.
- Run local counterfactual probes with the mathematically correct expected direction. Higher emissions cannot lower the one-step concentration (unconditional, since the derivative of the one-step update with respect to E is positive). For ventilation, the steady state obeys d(C_infinity)/d(k_vent) = [C_bg(k_dep + k_chem) - E/V] / k_tot^2, so the expected direction is conditional: when E/V > C_bg(k_dep + k_chem) (source-dominated), higher ventilation lowers C_infinity; when E/V < C_bg(k_dep + k_chem) (background-dominated), higher ventilation raises C_infinity toward C_bg; at equality it leaves it unchanged. The validator MUST compute the sign of E/V - C_bg(k_dep + k_chem) for each probe and assert the corresponding direction. Reference vectors cover both regimes.
- Test mean preservation of the observation model across a sufficiently large dedicated sample, with a confidence interval.
Observed trajectory correlations are not used as substitutes for equation-level monotonicity tests.
## 10.7 Coupling checks
The validator reports effect sizes and uncertainty for programmed couplings, including temperature/RH, occupancy/noise, and thermal deterrence/attendance. These are labelled `internal_consistency`.
## 10.8 Reference-scenario suite
Continuous integration SHALL run at least:
1. mild baseline;
2. heatwave;
3. stagnant air;
4. high wind;
5. crowded event;
6. traffic surge with typical occupancy;
7. empty-space edge case;
8. capacity rejection edge case;
9. incomplete-window edge case;
10. UTCI domain-clamping edge case.
The suite runs multiple seeds for statistical tests and one fixed seed for exact artifacts.
# 11. Scenario analysis
Sensitivity analysis SHOULD vary:
- deterrence coefficients;
- audience sensitivity values;
- dwell distribution;
- UHI magnitude;
- wind dependence and calm regularization;
- emissions and background concentrations;
- deposition/chemical loss;
- sensor noise and drift;
- capacity and admission mode;
- averaging windows;
- risk thresholds;
- episode segmentation; and
- priority parameters.
Results SHOULD report component-level action-band shares, occupancy/exposure distributions, strategy counts, priority ranks, and uncertainty over seeds. Directional plausibility is evidence of coherent model response, not proof of real-world accuracy.
# 12. Edge-case contract
The following behaviour is mandatory:
| Condition | Required behaviour |
|---|---|
| Zero occupancy | pedestrian sound energy is zero; audience counts are zero; dominant audience is null |
| No offered arrivals | NHPP test is not evaluated; occupancy still reflects initial visitors |
| Physical concentration zero | sensor reading zero in default model; log-ratio row excluded and counted |
| Incomplete rolling window | risk component unavailable unless explicit partial-risk policy is enabled |
| Capacity K <= 0 | configuration rejected |
| Wind zero | calm regularization applied; UTCI wind handled by domain policy |
| Equal audience counts | fixed documented audience precedence or null tie list; no unstable map-order tie break |
| Equal hazard maxima | all ties emitted in `co_dominant_issues`; precedence only selects convenience field |
| No qualifying episodes | valid empty strategy array and explanatory summary |
| WBGT in non-heat context | WBGT thermal action unavailable, not low |
| Episode touches file boundary | boundary-censored flag emitted |
| Fewer than 50 KS observations | `NOT_EVALUATED`, not pass |
| Invalid transition row | configuration rejected |
| Non-finite result | run fails before serialization |
| Deferred wait reaches maximum_defer_minutes | visitor exits queue as `deferred_timeout`; conservation identity of Section 6.10 holds |
| All enabled hazards unavailable | overall level null, completeness 0, provisional true, `insufficient_data` template |
| Polar day (no sunset) | UHI nocturnal indicator zero; flagged |
| Polar night (no sunrise) | h_peak from local solar midnight plus lag; indicator one; flagged |
| Audience knot interpolation across midnight | circular 24-hour interpolation; no discontinuity |
| Interpolated audience probability sum zero | configuration rejected at canonicalization |
| Background-dominated pollutant regime | ventilation probe expects concentration to rise toward C_bg (Section 10.6) |
# 13. Reproducibility contract
## 13.1 Exact reference-container reproducibility
Exact byte identity is guaranteed only when all of the following match:
- normalized scenario JSON;
- master seed;
- algorithm version;
- schema version;
- source commit;
- 64-bit reference container digest;
- dependency lockfile;
- canonical serialization profile.
The release process MUST publish at least one reference container digest and golden artifact hashes.
## 13.2 Cross-runtime numeric reproducibility
Outside the reference container, conformance is evaluated using:
- exact MT19937 unsigned integer vectors;
- exact event counts and categorical decisions where transcendental functions do not alter branch boundaries;
- per-field absolute and relative numeric tolerances for floating-point outputs;
- exact schema and logical invariants.
The tolerance map ships as `IoTSyn-v4_4_0-tolerance-map.json` and is normative for cross-runtime conformance; a release MUST republish it if any tolerance changes. A suggested baseline is absolute tolerance \(10^{-10}\) and relative tolerance \(10^{-10}\) for intermediate physical calculations before presentation rounding, but tighter or looser field-specific tolerances may be justified.
## 13.3 Canonical serialization
The profile fixes:
- locale `C`;
- UTF-8 without BOM;
- newline `\n`;
- fixed column order;
- fixed decimal digits per field;
- decimal point `.`;
- no grouping separators;
- negative zero normalized to zero;
- ISO 8601 timestamps with offset;
- canonical JSON object key ordering;
- compact JSON arrays in CSV;
- no non-finite values.
# 14. Security, privacy, and ethical use
All people and audience segments in a generated scenario are synthetic. Visitor IDs MUST be run-local pseudonymous identifiers. The platform SHOULD avoid generating attributes that could be mistaken for real personal data unless clearly labelled synthetic.
Audience sensitivity is an aggregate configurable planning assumption. It MUST NOT be used to make consequential decisions about identifiable individuals.
# 15. Scope and limitations
- The urban climate model is lumped and does not resolve street-canyon flow or detailed shading.
- The mean-radiant-temperature formula is an operational approximation.
- The pollutant box omits spatial gradients, complex chemistry, and plume dynamics.
- The traffic-emission mapping is scenario-level and requires calibration for city-specific use.
- The noise model is an energetic aggregate, not a propagation model.
- The pedestrian model represents aggregate arrivals and dwell, not trajectories or social interactions.
- Primary audience categories simplify overlapping identities.
- Operational thresholds are configurable planning bands, not universal medical thresholds.
- Strategy templates are design aids, not validated campaigns.
- External validity requires benchmarking against real local datasets.
# Appendix A. Known-answer and reference calculation vectors
The complete machine-readable set is `IoTSyn-v4_4_0-reference-test-vectors.json` (master seed "123456" for all stream-based vectors). The tables below reproduce a subset; the JSON file is authoritative.
## A.1 MT19937 seed 5489
The first ten unsigned 32-bit outputs MUST be:
| Index | uint32 | U_open |
|---:|---:|---:|
| 1 | 3499211612 | 0.81472369201947004 |
| 2 | 581869302 | 0.13547700422350317 |
| 3 | 3890346734 | 0.90579193423036486 |
| 4 | 3586334585 | 0.83500858990009874 |
| 5 | 545404204 | 0.12698681198526174 |
| 6 | 4161255391 | 0.96886777121108025 |
| 7 | 3922919429 | 0.91337585577275604 |
| 8 | 949333985 | 0.22103404288645834 |
| 9 | 2715962298 | 0.6323592500993982 |
| 10 | 1323567403 | 0.30816705047618598 |
Using the first two open uniforms with Box-Muller gives:
\[
Z_0=0.421908272819606,\qquad Z_1=0.481462264532713.
\]
## A.2 Pollutant one-step calculation
Inputs:
```text
C0 = 40 microgram/m3
E = 120000 microgram/s
H = 20 m
A = 10000 m2
Lx = 200 m
u_eff = 1.2 m/s
k_dep = 0.0001 s-1
k_chem = 0.00005 s-1
C_bg = 12 microgram/m3
dt = 900 s
```
Expected values:
```text
V = 200000 m3
k_vent = 0.006 s-1
k_total = 0.00615 s-1
C_infinity = 109.268292682926813 microgram/m3
C1 = 108.994945548141331 microgram/m3
```
## A.3 Risk example
Input vector:
```json
{"thermal":3,"air_quality":2,"noise":0,"crowding":2}
```
Expected result under precedence `thermal > air_quality > crowding > noise`:
```json
{
"overall_action_level": "severe",
"dominant_issue": "thermal",
"co_dominant_issues": ["thermal"],
"elevated_issues": ["thermal", "air_quality", "crowding"],
"elevated_hazard_count": 3,
"compound_risk_flag": true,
"risk_completeness_fraction": 1.0,
"overall_action_level_provisional": false
}
```
## A.4 Substream seed derivation (master_seed "123456")
Selected derived 32-bit seeds (salt "IoTSyn/v4.3.0", Section 5.1):
| Stream | seed32 |
|---|---:|
| meteorology.temperature | 892704917 |
| meteorology.wind | 3827278554 |
| crowd.state | 3633539532 |
| crowd.arrivals | 655598784 |
| crowd.dwell | 1289133551 |
| pollutant.pm25.source | 3576019562 |
| acoustics.residual | 22360979 |
All sixteen Core streams are enumerated in the JSON file.
## A.5 Distribution and NHPP vectors
The JSON file pins, per dedicated KAT stream: the first draws of the Normal (cache order visible across six values), Exponential, Weibull, LogNormal, Knuth Poisson (lambda 3.5), PTRS Poisson (lambda 100), Marsaglia-Tsang Gamma (alpha 2.5 and the alpha 0.7 boost path), Beta(2, 8), the categorical index sequence, phi_normal_cdf_v1 points with tolerance 5e-14, and one complete offered-arrival interval on `crowd.arrivals` (lambda 120 per hour, 900 s step: expected count 22, first sorted offsets listed).
## A.6 Solar vectors (iotsyn_solar_v1, Algiers 36.75 N, 3.06 E)
At 2026-07-14T12:00:00+01:00: declination 21.635022 deg, equation of time -5.907261 min, elevation 70.922460 deg, azimuth 138.707684 deg, sunset 1147.2798 minutes after 00:00 UTC. Two further instants are in the JSON file.
## A.7 Ventilation monotonicity, both regimes
Shared inputs: V 200000 m3, C_bg 12, k_dep 1e-4, k_chem 5e-5 (so C_bg times total loss is 0.0018).
| Regime | E (microgram/s) | C_inf at k_vent 0.006 | C_inf at k_vent 0.012 | Direction |
|---|---:|---:|---:|---|
| Source-dominated | 120000 | 109.268293 | 61.234568 | decreasing |
| Background-dominated | 200 | 11.869919 | 11.934156 | increasing toward C_bg |
## A.8 Miscellaneous physics vectors
Magnus saturation vapour pressure at 20 C: 2332.596022 Pa. Pedestrian wind for v10 3.0 m/s, z0 0.5 m, z 1.5 m: 1.100177374 m/s. Energetic sum of 55, 50, and 45 dB sources: 56.511331047 dB. Largest remainder for N 7 with probabilities 0.45/0.35/0.20: counts 3, 3, 1. Partial-availability and all-unavailable risk vectors, and a compound priority example, are in the JSON file.
# Appendix B. Minimum normalized scenario structure
```json
{
"schema_version": "4.4.0",
"algorithm_version": "iotsyn-smartcity-4.4.0",
"master_seed": "123456",
"time": {
"start_timestamp": "2026-07-14T00:00:00+01:00",
"step_seconds": 900,
"row_count": 672,
"timezone": "Africa/Algiers",
"warmup_duration_minutes": 1440
},
"location": {
"latitude_deg": 36.75,
"longitude_deg": 3.06,
"solar_position_algorithm_version": "iotsyn_solar_v1"
},
"occupancy": {
"initial_occupancy_mode": "sampled_stationary",
"space_capacity_persons": 500,
"capacity_mode": "report_only"
},
"thermal": {
"primary_index": "UTCI",
"utci_module_version": "utci_approx_v1",
"domain_policy": "clamp_and_flag"
},
"air_quality": {
"decision_profile": "iotsyn_operational_24h_v1",
"incomplete_window_policy": "require_complete_window"
},
"noise": {
"averaging_window_minutes": 15
},
"decision": {
"aggregation_rule": "conservative_max_v1",
"decision_mode": "ex_post",
"priority_algorithm_version": "compound_priority_v1"
},
"serialization": {
"profile": "canonical-json-csv-v1"
}
}
```
The real normalized file contains every resolved default, not only this minimum illustration.
# Appendix C. Implementation checklist
Before coding a module, its developer SHALL be able to answer:
1. What are the exact input and output units?
2. Which named random stream does it consume?
3. What is its initial condition?
4. What happens at zero, at the domain boundary, and outside the domain?
5. Is the output latent truth, observation, aggregation, or decision?
6. Which deterministic and statistical tests cover it?
7. Which version field changes if its behaviour changes?
8. Does it alter any previously stable random sequence?
9. Is a complete rolling history required?
10. Can every output be recomputed from stored artifacts?
# Appendix D. Normative equations of iotsyn_solar_v1
All angles are in degrees unless stated; deg/rad conversions are explicit. Inputs: Unix time in seconds (UTC), latitude phi (north positive), longitude L (east positive). No atmospheric refraction is applied to the emitted elevation; sunrise and sunset use the conventional zenith of 90.833 degrees (refraction plus solar radius). This module is deterministic and consumes no random stream.
```text
JD = unix_seconds/86400 + 2440587.5
T = (JD - 2451545)/36525
L0 = mod(280.46646 + T*(36000.76983 + 0.0003032*T), 360)
M = 357.52911 + T*(35999.05029 - 0.0001537*T)
ecc = 0.016708634 - T*(0.000042037 + 0.0000001267*T)
Cq = sin(M)*(1.914602 - T*(0.004817 + 0.000014*T))
+ sin(2M)*(0.019993 - 0.000101*T) + sin(3M)*0.000289
Ltrue = L0 + Cq
Omega = 125.04 - 1934.136*T
Lapp = Ltrue - 0.00569 - 0.00478*sin(Omega)
eps0 = 23 + (26 + (21.448 - T*(46.815 + T*(0.00059 - 0.001813*T)))/60)/60
eps = eps0 + 0.00256*cos(Omega)
decl = asin( sin(eps)*sin(Lapp) )
y = tan(eps/2)^2
EoT = 4*deg( y*sin(2*L0) - 2*ecc*sin(M) + 4*ecc*y*sin(M)*cos(2*L0)
- 0.5*y^2*sin(4*L0) - 1.25*ecc^2*sin(2*M) ) # minutes
TST = mod( minutes_of_utc_day + EoT + 4*L, 1440 )
HA = TST/4 - 180; if HA < -180 then HA = HA + 360
cosZ = sin(phi)*sin(decl) + cos(phi)*cos(decl)*cos(HA)
elev = 90 - deg(acos(clamp(cosZ, -1, 1)))
Z = deg(acos(clamp(cosZ, -1, 1))) # zenith
acosA = deg(acos(clamp( (sin(phi)*cosZ - sin(decl)) / (cos(phi)*sin(Z)), -1, 1)))
azim = mod(acosA + 180, 360) if HA > 0 else mod(540 - acosA, 360)
arg = cos(90.833)/(cos(phi)*cos(decl)) - tan(phi)*tan(decl)
HA0 = deg(acos(arg)) if -1 <= arg <= 1 else undefined (polar case)
noonU = 720 - 4*L - EoT # minutes UTC
sunriseU = noonU - 4*HA0 ; sunsetU = noonU + 4*HA0
```
When HA0 is undefined the polar rules of Section 6.4 apply. Known-answer vectors: Appendix A.6.
# References
- Andreasen, A.R. (1995). *Marketing Social Change*. Jossey-Bass.
- Box, G.E.P. and Muller, M.E. (1958). A note on the generation of random normal deviates. *Annals of Mathematical Statistics*, 29(2), 610-611.
- Bröde, P. et al. (2012). Deriving the operational procedure for the Universal Thermal Climate Index. *International Journal of Biometeorology*, 56(3), 481-494.
- Brown, E.N. et al. (2002). The time-rescaling theorem and its application to neural spike train data analysis. *Neural Computation*, 14(2), 325-346.
- Fischer, W. and Meier-Hellstern, K. (1993). The Markov-modulated Poisson process cookbook. *Performance Evaluation*, 18(2), 149-171.
- Holm, S. (1979). A simple sequentially rejective multiple test procedure. *Scandinavian Journal of Statistics*, 6(2), 65-70.
- Hörmann, W. (1993). The transformed rejection method for generating Poisson random variables. *Insurance: Mathematics and Economics*, 12(1), 39-45.
- ISO 1996-1:2016. *Acoustics - Description, measurement and assessment of environmental noise - Part 1*.
- ISO 7243:2017. *Ergonomics of the thermal environment - Assessment of heat stress using the WBGT index*.
- Jendritzky, G., de Dear, R. and Havenith, G. (2012). UTCI - Why another thermal index? *International Journal of Biometeorology*, 56(3), 421-428.
- Kasten, F. and Czeplak, G. (1980). Solar and terrestrial radiation dependent on the amount and type of cloud. *Solar Energy*, 24(2), 177-189.
- Kolmogorov, A. (1933). Sulla determinazione empirica di una legge di distribuzione. *Giornale dell'Istituto Italiano degli Attuari*, 4, 83-91.
- Kotler, P. and Zaltman, G. (1971). Social marketing: an approach to planned social change. *Journal of Marketing*, 35(3), 3-12.
- Lewis, P.A.W. and Shedler, G.S. (1979). Simulation of nonhomogeneous Poisson processes by thinning. *Naval Research Logistics Quarterly*, 26(3), 403-413.
- Liljegren, J.C. et al. (2008). Modeling the wet bulb globe temperature using standard meteorological measurements. *Journal of Occupational and Environmental Hygiene*, 5(10), 645-655.
- Marsaglia, G. and Tsang, W.W. (2000). A simple method for generating gamma variables. *ACM Transactions on Mathematical Software*, 26(3), 363-372.
- Matsumoto, M. and Nishimura, T. (1998). Mersenne twister: a 623-dimensionally equidistributed uniform pseudo-random number generator. *ACM Transactions on Modeling and Computer Simulation*, 8(1), 3-30.
- Meeus, J. (1998). *Astronomical Algorithms* (2nd ed.). Willmann-Bell.
- Oke, T.R. (1982). The energetic basis of the urban heat island. *Quarterly Journal of the Royal Meteorological Society*, 108(455), 1-24.
- Ott, W.R. (1990). A physical explanation of the lognormality of pollutant concentrations. *Journal of the Air and Waste Management Association*, 40(10), 1378-1383.
- Rundgren, A., Jordan, B. and Erdtman, S. (2020). *JSON Canonicalization Scheme (JCS)*. RFC 8785.
- Sonntag, D. (1990). Important new values of the physical constants of 1986, vapour pressure formulations based on the ITS-90, and psychrometer formulae. *Zeitschrift für Meteorologie*, 40(5), 340-344.
- Tetens, O. (1930). Über einige meteorologische Begriffe. *Zeitschrift für Geophysik*, 6, 297-309.
- World Health Organization (2021). *WHO Global Air Quality Guidelines*.
- World Meteorological Organization (2018). *Guide to Instruments and Methods of Observation* (WMO-No. 8).
---
**IoTSyn v4.4.0-RC1 - Smart City & Public Spaces Edition**
**Methodology and Normative Implementation Specification**
**Release Candidate 1 - 2026-07-04**