Skip to content

Commit

Permalink
Merge branch 'master' into dev-solver
Browse files Browse the repository at this point in the history
  • Loading branch information
prisae committed Oct 14, 2020
2 parents f088724 + 907839c commit 96cdb1b
Show file tree
Hide file tree
Showing 12 changed files with 161 additions and 245 deletions.
14 changes: 14 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,20 @@ recent versions
"""""""""""""""


*latest (dev, will become v0.14.0)*
-----------------------------------

- ``models.Model``: Removed deprecated parameters ``res_{x;y;z}``.

- ``io.save``:

- Removed deprecated parameter ``backend`` in ``save``.
- File extension has to be provided in ``save``, no default any longer.

- ``meshes``: Deprecated the old meshing routines ``get_stretched_h``,
``get_domain``, ``get_hx``; will be removed in the future.


*v0.13.0* : CLI
---------------

Expand Down
61 changes: 14 additions & 47 deletions emg3d/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@

import os
import json
import warnings
from datetime import datetime

import numpy as np
Expand All @@ -46,7 +45,7 @@
}


def save(fname, backend=None, compression="gzip", **kwargs):
def save(fname, **kwargs):
"""Save surveys, meshes, models, fields, and more to disk.
Serialize and save data to disk in different formats (see parameter
Expand All @@ -66,18 +65,14 @@ def save(fname, backend=None, compression="gzip", **kwargs):
File name inclusive ending, which defines the used data format.
Implemented are currently:
- `.h5` (default): Uses `h5py` to store inputs to a hierarchical,
compressed binary hdf5 file. Recommended file format, but requires
the module `h5py`. Default format if ending is not provided or not
recognized.
- `.h5`: Uses `h5py` to store inputs to a hierarchical, compressed
binary hdf5 file. Recommended file format, but requires the module
`h5py`.
- `.npz`: Uses `numpy` to store inputs to a flat, compressed binary
file. Default format if `h5py` is not installed.
file.
- `.json`: Uses `json` to store inputs to a hierarchical, plain text
file.
backend : deprecated
Set the appropriate file-ending in `fname` instead.
compression : int or str, optional
Passed through to h5py, default is 'gzip'.
Expand All @@ -104,6 +99,7 @@ def save(fname, backend=None, compression="gzip", **kwargs):
"""
# Get and remove optional kwargs.
compression = kwargs.pop('compression', 'gzip')
json_indent = kwargs.pop('json_indent', 2)
collect_classes = kwargs.pop('collect_classes', False)
verb = kwargs.pop('verb', 1)
Expand All @@ -120,45 +116,16 @@ def save(fname, backend=None, compression="gzip", **kwargs):
# sorted TensorMesh, Field, and Model instances.
data = _dict_serialize(kwargs, collect_classes=collect_classes)

# Deprecated backends.
if backend is not None:
mesg = ("\n The use of `backend` is deprecated and will be removed."
"\n Use the file-endings [`.h5`, `.npz`, `.json`] instead.")
warnings.warn(mesg, DeprecationWarning)

if backend == 'numpy':
backend = 'npz'
elif backend == 'h5py':
backend = 'h5'
else:
backend = backend

# Add file-ending if necessary.
if not full_path.endswith('.'+backend):
full_path += '.'+backend

else:

# Get backend from `fname`.
if full_path.split('.')[-1] in ['npz', 'h5', 'json']:
backend = full_path.split('.')[-1]
else: # Fallback to default.
if isinstance(h5py, str):
backend = 'npz'
else:
backend = 'h5'
full_path += '.'+backend

# Save data depending on the backend.
if backend == "npz":
# Save data depending on the extension.
if full_path.endswith('.npz'):

# Convert hierarchical dict to a flat dict.
data = _dict_flatten(data)

# Store flattened data.
np.savez_compressed(full_path, **data)

elif backend == "h5":
elif full_path.endswith('.h5'):

# Check if h5py is installed.
if isinstance(h5py, str):
Expand All @@ -168,7 +135,7 @@ def save(fname, backend=None, compression="gzip", **kwargs):
with h5py.File(full_path, "w") as h5file:
_hdf5_add_to(data, h5file, compression)

elif backend == "json":
elif full_path.endswith('.json'):

# Move arrays to lists and decompose complex data.
data = _dict_dearray_decomp(data)
Expand All @@ -178,7 +145,8 @@ def save(fname, backend=None, compression="gzip", **kwargs):
json.dump(data, f, indent=json_indent)

else:
raise ValueError(f"Unknown backend '{backend}'.")
ext = full_path.split('.')[-1]
raise ValueError(f"Unknown extension '.{ext}'.")

# Print file info.
if verb > 0:
Expand All @@ -197,8 +165,7 @@ def load(fname, **kwargs):
Parameters
----------
fname : str
File name including extension. Used backend depends on the file
extensions:
File name including extension. Possibilities:
- '.npz': numpy-binary
- '.h5': h5py-binary (needs `h5py`)
Expand Down Expand Up @@ -696,7 +663,7 @@ def _hdf5_get_from(h5file):
def _compare_dicts(dict1, dict2, verb=False, **kwargs):
"""Return True if the two dicts `dict1` and `dict2` are the same.
Private method, not foolproof. Useful for developing new backends.
Private method, not foolproof. Useful for developing new extensions.
If `verb=True`, it prints it key starting with the following legend:
Expand Down
31 changes: 21 additions & 10 deletions emg3d/meshes.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
# License for the specific language governing permissions and limitations under
# the License.

import warnings
from copy import deepcopy

import numpy as np
Expand All @@ -31,6 +32,11 @@ class dTensorMesh:
__all__ = ['TensorMesh', 'get_hx_h0', 'get_cell_numbers', 'get_stretched_h',
'get_domain', 'get_hx']

MESHWARNING = (
"\n `get_stretched_h`, `get_domain`, and `get_hx` are"
"\n deprecated and will be removed. Use `get_hx_h0` instead."
)


class _TensorMesh:
"""Minimal TensorMesh for internal multigrid computation.
Expand Down Expand Up @@ -599,7 +605,7 @@ def get_stretched_h(min_width, domain, nx, x0=0, x1=None, resp_domain=False):
Returns `nx` cell widths within `domain`, where the minimum cell width is
`min_width`. The cells are not stretched within `x0` and `x1`, and outside
uses a power-law stretching. The actual stretching factor and the number of
cells left and right of `x0` and `x1` are find in a minimization process.
cells left and right of `x0` and `x1` are found in a minimization process.
The domain is not completely respected. The starting point of the domain
is, but the endpoint of the domain might slightly shift (this is more
Expand Down Expand Up @@ -656,6 +662,7 @@ def get_stretched_h(min_width, domain, nx, x0=0, x1=None, resp_domain=False):
Cell widths of mesh.
"""
warnings.warn(MESHWARNING, DeprecationWarning)

# Cast to arrays
domain = np.array(domain, dtype=np.float64)
Expand Down Expand Up @@ -806,6 +813,7 @@ def get_domain(x0=0, freq=1, res=0.3, limits=None, min_width=None,
Start- and end-points of computation domain.
"""
warnings.warn(MESHWARNING, DeprecationWarning)

# Set fact_pos to fact_neg if not provided.
if fact_pos is None:
Expand Down Expand Up @@ -835,11 +843,11 @@ def get_domain(x0=0, freq=1, res=0.3, limits=None, min_width=None,
def get_hx(alpha, domain, nx, x0, resp_domain=True):
r"""Return cell widths for given input.
Find the number of cells left and right of `x0`, `nl` and `nr`
respectively, for the provided alpha. For this, we solve
Find the number of cells left (``nl``) and right (``nr``) of the center
``x0`` for the provided alpha. For this, we solve
.. math:: \frac{x_\text{max}-x_0}{x_0-x_\text{min}} =
\frac{a^{nr}-1}{a^{nl}-1}
\frac{a^\text{nr}-1}{a^\text{nl}-1}
where :math:`a = 1+\alpha`.
Expand All @@ -851,17 +859,17 @@ def get_hx(alpha, domain, nx, x0, resp_domain=True):
Stretching factor `a` is given by ``a=1+alpha``.
domain : list
[start, end] of model domain.
``[x_min, x_max]`` of model domain.
nx : int
Number of cells.
x0 : float
Center of the grid. `x0` is restricted to `domain`.
Center of the grid. ``x0`` is restricted to ``domain``.
resp_domain : bool
If False (default), then the domain-end might shift slightly to assure
that the same stretching factor is applied throughout. If set to True,
If False, then the domain-end might shift slightly to assure that the
same stretching factor is applied throughout. If set to True (default),
however, the domain is respected absolutely. This will introduce one
stretch-factor which is different from the other stretch factors, to
accommodate the restriction. This one-off factor is between the left-
Expand All @@ -871,9 +879,12 @@ def get_hx(alpha, domain, nx, x0, resp_domain=True):
Returns
-------
hx : ndarray
Cell widths of mesh.
Cell widths of mesh. All points are given by
``np.r_[xmin, xmin+np.cumsum(hx)]``
"""
warnings.warn(MESHWARNING, DeprecationWarning)

if alpha <= 0.: # If alpha <= 0: equal spacing (no stretching at all)
hx = np.ones(nx)*np.diff(np.squeeze(domain))/nx

Expand All @@ -882,7 +893,7 @@ def get_hx(alpha, domain, nx, x0, resp_domain=True):

# Get hx depending if x0 is on the domain boundary or not.
if np.isclose(x0, domain[0]) or np.isclose(x0, domain[1]):
# Get al a's
# Get all a's
alr = np.diff(domain)*alpha/(a**nx-1)*a**np.arange(nx)
if x0 == domain[1]:
alr = alr[::-1]
Expand Down
48 changes: 0 additions & 48 deletions emg3d/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
# License for the specific language governing permissions and limitations under
# the License.

import warnings
from copy import deepcopy

import numpy as np
Expand Down Expand Up @@ -89,29 +88,10 @@ class Model:
"""

_res_warning = (
"\n The keywords `res_{x;y;z}` are deprecated and will be removed."
"\n Use the keywords `property_{x;y;z}` instead."
)

def __init__(self, grid, property_x=1., property_y=None, property_z=None,
mu_r=None, epsilon_r=None, mapping='Resistivity', **kwargs):
"""Initiate a new model."""

# Check kwargs; purely for backwards compatibility.
if any([key.startswith('res') for key in kwargs.keys()]):
warnings.warn(self._res_warning, DeprecationWarning)

res_x = kwargs.pop('res_x', None)
res_y = kwargs.pop('res_y', None)
res_z = kwargs.pop('res_z', None)

property_x = property_x if res_x is None else res_x
property_y = property_y if res_y is None else res_y
property_z = property_z if res_z is None else res_z

mapping = 'Resistivity'

# Ensure no kwargs left.
if kwargs:
raise TypeError(f"Unexpected **kwargs: {list(kwargs.keys())}")
Expand Down Expand Up @@ -295,15 +275,6 @@ def from_dict(cls, inp):
"""
try:
# Check `res`; purely for backwards compatibility.
if any([key.startswith('res') for key in inp]):
warnings.warn(cls._res_warning, DeprecationWarning)

inp['property_x'] = inp.pop('res_x', None)
inp['property_y'] = inp.pop('res_y', None)
inp['property_z'] = inp.pop('res_z', None)
inp['mapping'] = 'Resistivity'

return cls(grid=inp['vnC'], property_x=inp['property_x'],
property_y=inp['property_y'],
property_z=inp['property_z'], mu_r=inp['mu_r'],
Expand Down Expand Up @@ -367,25 +338,6 @@ def property_z(self, property_z):
self._property_z = self._check_parameter(
property_z, 'property_z', True)

# Backwards compatibility for deprecated attributes.
@property
def res_x(self):
warnings.warn(self._res_warning, DeprecationWarning)
fmap = maps.MapResistivity()
return fmap.forward(self.map.backward(self.property_x))

@property
def res_y(self):
warnings.warn(self._res_warning, DeprecationWarning)
fmap = maps.MapResistivity()
return fmap.forward(self.map.backward(self.property_y))

@property
def res_z(self):
warnings.warn(self._res_warning, DeprecationWarning)
fmap = maps.MapResistivity()
return fmap.forward(self.map.backward(self.property_z))

# MAGNETIC PERMEABILITIES
@property
def mu_r(self):
Expand Down

0 comments on commit 96cdb1b

Please sign in to comment.