##~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~##
## ##
## This file forms part of the Underworld geophysics modelling application. ##
## ##
## For full license and copyright information, please refer to the LICENSE.md file ##
## located at the project root, or contact the authors. ##
## ##
##~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~##
"""
Utilities to convert between dimensional and non-dimensional values.
"""
from __future__ import print_function, absolute_import
import underworld as uw
from ._utils import TransformedDict
from pint import UnitRegistry
u = UnitRegistry()
COEFFICIENTS = None
pint_degc_labels = ['degC', 'degreeC', 'degree_Celsius', 'celsius']
[docs]def get_coefficients():
"""
Returns the global scaling dictionary.
"""
global COEFFICIENTS
if COEFFICIENTS is None:
COEFFICIENTS = TransformedDict()
COEFFICIENTS["[length]"] = 1.0 * u.meter
COEFFICIENTS["[mass]"] = 1.0 * u.kilogram
COEFFICIENTS["[time]"] = 1.0 * u.year
COEFFICIENTS["[temperature]"] = 1.0 * u.degK
COEFFICIENTS["[substance]"] = 1.0 * u.mole
return COEFFICIENTS
[docs]def non_dimensionalise(dimValue):
"""
Non-dimensionalize (scale) provided quantity.
This function uses pint to perform a dimension analysis and
return a value scaled according to a set of scaling coefficients.
Parameters
----------
dimValue : pint.Quantity
A pint quantity.
Returns
-------
float
The scaled value.
Example
-------
>>> import underworld as uw
>>> u = uw.scaling.units
>>> # Characteristic values of the system
>>> half_rate = 0.5 * u.centimeter / u.year
>>> model_height = 600e3 * u.meter
>>> refViscosity = 1e24 * u.pascal * u.second
>>> surfaceTemp = 0. * u.kelvin
>>> baseModelTemp = 1330. * u.kelvin
>>> baseCrustTemp = 550. * u.kelvin
>>> KL_meters = model_height
>>> KT_seconds = KL_meters / half_rate
>>> KM_kilograms = refViscosity * KL_meters * KT_seconds
>>> Kt_degrees = (baseModelTemp - surfaceTemp)
>>> K_substance = 1. * u.mole
>>> scaling_coefficients = uw.scaling.get_coefficients()
>>> scaling_coefficients["[time]"] = KT_seconds
>>> scaling_coefficients["[length]"] = KL_meters
>>> scaling_coefficients["[mass]"] = KM_kilograms
>>> scaling_coefficients["[temperature]"] = Kt_degrees
>>> scaling_coefficients["[substance]"] -= K_substance
>>> # Get a scaled value:
>>> gravity = uw.scaling.non_dimensionalise(9.81 * u.meter / u.second**2)
"""
try:
val = dimValue.unitless
if val:
return dimValue
except AttributeError:
return dimValue
dimValue = dimValue.to_base_units()
scaling_coefficients = get_coefficients()
length = scaling_coefficients["[length]"]
time = scaling_coefficients["[time]"]
mass = scaling_coefficients["[mass]"]
temperature = scaling_coefficients["[temperature]"]
substance = scaling_coefficients["[substance]"]
length = length.to_base_units()
time = time.to_base_units()
mass = mass.to_base_units()
temperature = temperature.to_base_units()
substance = substance.to_base_units()
@u.check('[length]', '[time]', '[mass]', '[temperature]', '[substance]')
def check(length, time, mass, temperature, substance):
return
check(length, time, mass, temperature, substance)
# Get dimensionality
dlength = dimValue.dimensionality['[length]']
dtime = dimValue.dimensionality['[time]']
dmass = dimValue.dimensionality['[mass]']
dtemp = dimValue.dimensionality['[temperature]']
dsubstance = dimValue.dimensionality['[substance]']
factor = (length**(-dlength) *
time**(-dtime) *
mass**(-dmass) *
temperature**(-dtemp) *
substance**(-dsubstance))
dimValue *= factor
if dimValue.unitless:
return dimValue.magnitude
else:
raise ValueError('Dimension Error')
[docs]def dimensionalise(value, units):
"""
Dimensionalise a value.
Parameters
----------
value : float, int
The value to be assigned units.
units : pint units
The units to be assigned.
Returns
-------
pint quantity: dimensionalised value.
Example
-------
>>> import underworld as uw
>>> A = uw.scaling.dimensionalise(1.0, u.metre)
"""
unit = (1.0 * units).to_base_units()
scaling_coefficients = get_coefficients()
length = scaling_coefficients["[length]"]
time = scaling_coefficients["[time]"]
mass = scaling_coefficients["[mass]"]
temperature = scaling_coefficients["[temperature]"]
substance = scaling_coefficients["[substance]"]
length = length.to_base_units()
time = time.to_base_units()
mass = mass.to_base_units()
temperature = temperature.to_base_units()
substance = substance.to_base_units()
@u.check('[length]', '[time]', '[mass]', '[temperature]', '[substance]')
def check(length, time, mass, temperature, substance):
return
# Check that the scaling parameters have the correct dimensions
check(length, time, mass, temperature, substance)
# Get dimensionality
dlength = unit.dimensionality['[length]']
dtime = unit.dimensionality['[time]']
dmass = unit.dimensionality['[mass]']
dtemp = unit.dimensionality['[temperature]']
dsubstance = unit.dimensionality['[substance]']
factor = (length**(dlength) *
time**(dtime) *
mass**(dmass) *
temperature**(dtemp) *
substance**(dsubstance))
if (isinstance(value, uw.mesh._meshvariable.MeshVariable) or
isinstance(value, uw.swarm._swarmvariable.SwarmVariable)):
tempVar = value.copy()
tempVar.data[...] = (value.data[...] * factor).to(units).magnitude
return tempVar
else:
return (value * factor).to(units)
def ndargs(f):
""" Decorator used to non-dimensionalise the arguments of a function"""
def convert(obj):
if isinstance(obj, (list, tuple)):
return type(obj)([convert(val) for val in obj])
else:
return non_dimensionalise(obj)
def new_f(*args, **kwargs):
nd_args = [convert(arg) for arg in args]
nd_kwargs = {name:convert(val) for name, val in kwargs.items()}
return f(*nd_args, **nd_kwargs)
new_f.__name__ = f.__name__
return new_f