AUBELLHOP: environment configuration

Each Bellhop simulation is driven by its so-called environment configuration file. This is created by aubellhop based on the setup of the respective Environment class.

This page outlines how to create, edit, and manage instances of the Environment class. The environment parameters page details each of the options available.

1 Creating new environments

The Python front-end to Bellhop provides a “default” environment to allow quick setup for demonstration purposes:

import aubellhop as bh
env = bh.Environment()
assert isinstance(env, bh.Environment)

2 Environment methods

The Environment class provides the following user-facing class methods:

  • Environment.from_file(fname) — read environment configuration in original Bellhop .env text format (also reading in additional .ssp, .bty files if needed)
  • Environment.from_dict(env_dict) — read environment configuration from dictionary of fields (see: to_dict() below)

The Environment class provides the following user-facing instance methods:

  • Environment().check() — finalise and validate parameters (semi-automatic, see below)
  • Environment().copy() — shallow copy the instance
  • Environment().defaults() — fill None fields with a default setting if there is one (use with care)
  • Environment().reset() — set all user-facing values from the instance to None
  • Environment().to_dict() — convert parameters to a standard dict
  • Environment().to_file(fname, task=None) — write environment configuration to file(s) in original Bellhop .env text format (also writing additional .ssp, .bty files if needed)
  • Environment().unwrap(fields) — converts a field list into a list of envs with single values

To scan over the full list of user-facing fields, the standard print() function will return something sensible:

> env = bh.Environment()
> print(env)
>
    ...
 'frequency': 25000.0,
 'grid_type': 'default',
 'interference_mode': None,
 'name': 'bellhop/python default',
 'receiver_depth': 10.0,
    ...

3 Setting environment parameters

The parameters of the environment can be set via arguments to this initialisation function:

env = bh.Environment(frequency=500) # Hz

These parameters can also be changed using both class- and dict-based setting approaches:

env.frequency = 500    # okay
env['frequency'] = 500 # also okay

Parameters can be queried in the same way:

Code
import aubellhop as bh
env = bh.Environment()

env.frequency = 600
print(f"{env.frequency =}")
env.frequency = 700
print(f"{env['frequency'] =}")
env.frequency =600
env['frequency'] =700

The Environment class does not allow unrecognised fields to be set:

env.quefrency = 500  # ERROR

Some fields take a hard-coded list of string options:

env.soundspeed_interp = "spline"  # okay

And again unrecognised options result in an error:

env.soundspeed_interp = "plines"  # ERROR

4 Parameter checking — .check()

Before standard functions for running Bellhop computations, or otherwise working with the environment configuration data, the method .check() will be called on the Environment instance. This function performs two key tasks:

  1. Final assignments of implicitly-linked parameters (for instance, setting max_depth to the maximum of the depth field)
  2. A suite of consistency checks and assertions

Most users will be unlikely to explicitly need to use this function. However, if there are situations where various parameters are set and then the environment needs to be copied or queried, it would be good practice (or sometimes necessary) to run it directly. For instance:

env1 = bh.Environment()
range_vec = np.linspace(0,5000) # 5km simulation
depth_vec = np.linspace(1000,2000) # ramp seabed
env1.bottom_depth = np.column_stack([range_vec,depth_vec])
env1.check()
env2 = env1.copy()
assert env2._depth_max == 2000

5 Reading and writing — .from_file() & .to_file()

The traditional Bellhop interface used ASCII fname.env files for environment configuration, with additional files to define complex soundspeed profiles, bathymetry, altimetry, etc. The Python interface can read a seamount.env file by initialising an empty environment and using the .from_file() class method:

env = bh.Environment.from_file('seamount.env')

After setting up an environment, you may wish to write it to a file for debugging purposes, or to pass it along to another of the Acoustic Toolbox programs (such as KRAKEN).

env = bh.Environment(...)
env.to_file("kraken.env", task="rays")

The step is performed automatically, writing the file(s) to a temporary location, when performing a .compute() step to execute the simulation.

The read/write chain should allow round-trip compatibility, although there may be some differences:

  • Floating point or fixed digit calculations may be quantised differently
  • Certain Bellhop option flags may be implicit in one representation and explicit in another
  • The comments will be different
  • There may be bugs :)

6 Storing and loading — .from_dict() & .to_dict()

For Python-only workflows, using dictionaries as an intermediate format is likely more natural that text files. Passing dictionaries of environment fields is likely to be most useful when defining standard environments for later reuse, or when constructing complex environments based on external data.

env1 = bh.Environment(...)
dict1 = env1.to_dict()
# manipulate dict1
env2 = bh.Environment.from_dict(dict1)

A monte carlo analysis might generate thousands of environments, in which case this interface could be used to serialise to disk each environment via a dictionary to allow the simulations to be repeated or reproduced.

7 Copying and clearing — .copy() & .reset()

Shallow copying of one environment to another can be achieved with:

env1 = bh.Environment(...)
env2 = env1.copy()

All fields from an environment can be completely cleared using .reset(). This is unlikely to be useful except when loading settings from another source and wanting to ensure there are all parameters need to be explicitly set.

8 Unwrapping list-based parameters — .unwrap()

Bellhop often can only handle single values at a time for parameters such as frequency, source position, and so on. The .unwrap method allows such parameters to be defined as lists, and then create a list of environments. An example will explain more clearly. These two definitions are equivalent:

env = bh.Environment()
env.frequency = [100, 200]
envs = env.unwrap('frequency')

env1 = bh.Environment()
env2 = bh.Environment()
env1.frequency = 100
env2.frequency = 200
envs = [env1, env2]

Moreover, multiple fields can be unwrapped at once to result in every combination of defined parameters:

env = bh.Environment()
env.frequency = [100, 200, 300]
env.source_depth = [5, 10]
envs = env.unwrap('frequency','source_depth')
# results in a list of 6 Environments for 3x2 combination of options

These lists of environments are intended to be useful when used as inputs into the bellhop.compute function. The env.name field is unique for each Environment in the list.