Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 3 additions & 27 deletions workflow/scripts/hf_sim.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,32 +247,6 @@ def hf_simulate_station(
return station_name, epicentre_distance, station_waveform


def stable_hash(station: str) -> int:
"""Compute stable hashes for station names.

The HF binary expects seeds. We want the provided seed to be
independent of the order of stations in the stations lists. This
is so setting HF seed reproduces the same outputs, even for
different orders or subsets of the original station file. To do
that, we generate stable hashes based on the station name.


Parameters
----------
station : str
The station name.

Returns
-------
int
A hash of the station name. This is guaranteed to be in the
range of a signed 32-bit integer.
"""
return int.from_bytes(
hashlib.blake2b(station.encode("utf-8"), digest_size=4).digest(), signed=True
)


def station_seeds(seed: int, stations: Iterable[str]) -> npt.NDArray[np.int32]:
"""Create a list of per-station seeds in an order-invariant fashion with a root seed.

Expand All @@ -289,7 +263,9 @@ def station_seeds(seed: int, stations: Iterable[str]) -> npt.NDArray[np.int32]:
npt.NDArray[np.int32]
A list of station seeds.
"""
station_hashes = np.array([stable_hash(name) for name in stations], dtype=np.int32)
station_hashes = np.array(
[utils.stable_hash(name) for name in stations], dtype=np.int32
)
# Rather than add (which could overflow and cause annoying numpy
# warnings), we just xor the hf seed with the station hashes.
# Since this is invertible, we ensure that the same hf seed gives
Expand Down
5 changes: 4 additions & 1 deletion workflow/scripts/realisation_to_srf.py
Original file line number Diff line number Diff line change
Expand Up @@ -546,7 +546,10 @@ def generate_fault_srf(
gsf_file_path=gsf_file_path,
nx=nx,
ny=ny,
seed=environment.seeds.genslip_seed,
# NOTE: This stable hash trick is also used in hf-sim, and is
# designed to give order invariant stable hashes for segments
# based on their names.
seed=environment.seeds.genslip_seed ^ utils.stable_hash(name),
velocity_model_path=environment.velocity_model_path,
shypo=genslip_hypocentre_coords[0],
dhypo=genslip_hypocentre_coords[1],
Expand Down
22 changes: 22 additions & 0 deletions workflow/utils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Miscellaneous workflow utilities that couldn't go anywhere else."""

import hashlib
import os
import tempfile
import urllib.request
Expand Down Expand Up @@ -174,3 +175,24 @@ def dict_zip(*dicts: Mapping[K, Any], strict: bool = True) -> dict[K, tuple[Any,

result = {key: tuple(d[key] for d in dicts) for key in list(keys)}
return result


def stable_hash(value: str, size: int = 4) -> int:
"""Compute stable hashes for strings.

Parameters
----------
value : str
String to hash.
size : int
Size of the hash value.

Returns
-------
int
A hash of the value. Guaranteed to be an integer within
the signed integer bounds prescribed by ``size``.
"""
return int.from_bytes(
hashlib.blake2b(value.encode("utf-8"), digest_size=size).digest(), signed=True
)
Loading