##~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~##
## ##
## 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. ##
## ##
##~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~#~##
import underworld as uw
import underworld._stgermain as _stgermain
from . import sle
import underworld.libUnderworld as libUnderworld
from underworld.libUnderworld import petsc
from ._options import Options
from mpi4py import MPI
[docs]class HeatSolver(_stgermain.StgCompoundComponent):
"""
Steady State Heat Equation Solver.
"""
_objectsDict = {"_heatsolver" : "Energy_SLE_Solver" }
_selfObjectName = "_heatsolver"
def __init__(self, heatSLE, **kwargs):
if not isinstance(heatSLE, (uw.systems.SteadyStateHeat, uw.utils.MeshVariable_Projection,uw.systems.SteadyStateDarcyFlow, uw.utils.SolveLinearSystem)):
raise TypeError("Provided system must be of 'SteadyStateHeat' class")
self._heatSLE=heatSLE
self.options=OptionsGroup()
super(HeatSolver, self).__init__(**kwargs)
def _add_to_stg_dict(self,componentDictionary):
# call parents method
super(HeatSolver,self)._add_to_stg_dict(componentDictionary)
[docs] def solve(self, nonLinearIterate=None, nonLinearTolerance=1.0e-2, nonLinearMaxIterations=500, callback_post_solve=None, **kwargs):
"""
Solve the HeatEq system
Parameters
----------
nonLinearIterate: bool
True will perform non linear iterations iterations, False (or 0) will not
nonLinearTolerance: float, Default=1.0e-2
Relative tolerance criterion for the change in the velocity field
nonLinearMaxIterations: int, Default=500
Maximum number of non linear iteration to perform
callback_post_solve: func, Default=None
Optional callback function to be performed at the end of a linear solve iteration.
Commonly this will be used to perform operations between non linear iterations, for example,
calibrating the solution or removing the system null space.
"""
# error check callback_post_solve
if callback_post_solve is not None:
if not callable(callback_post_solve):
raise RuntimeError("The 'callback_post_solve' parameter is not 'None' and isn't callable")
# in this c function we handle callback_post_solve=None
uw.libUnderworld.StgFEM.SystemLinearEquations_SetCallback(self._heatSLE._cself, callback_post_solve)
if not isinstance(nonLinearTolerance, float) or nonLinearTolerance < 0.0:
raise ValueError("'nonLinearTolerance' option must be of type 'float' and greater than 0.0")
# Set up options string from dictionaries.
# We set up here so that we can set/change terms on the dictionaries before we run solve
self._setup_options(**kwargs)
petsc.OptionsClear() # reset the petsc options
petsc.OptionsInsertString(self._optionsStr)
libUnderworld.StgFEM.Energy_SLE_Solver_SetSolver(self._cself, self._heatSLE._cself) # this sets solver on SLE struct.
# check for non-linearity
nonLinear = self._check_linearity(nonLinearIterate)
if nonLinear and nonLinearIterate:
libUnderworld.StgFEM.SystemLinearEquations_SetNonLinearTolerance(self._heatSLE._cself, nonLinearTolerance)
libUnderworld.StgFEM.SystemLinearEquations_SetToNonLinear(self._heatSLE._cself, True )
self._heatSLE._cself.nonLinearMaxIterations = nonLinearMaxIterations
else:
libUnderworld.StgFEM.SystemLinearEquations_SetToNonLinear(self._heatSLE._cself, False )
if self._heatSLE._swarm:
self._heatSLE._swarm._voronoi_swarm.repopulate()
libUnderworld.StgFEM.SystemLinearEquations_BC_Setup(self._heatSLE._cself, None)
libUnderworld.StgFEM.SystemLinearEquations_LM_Setup(self._heatSLE._cself, None)
libUnderworld.StgFEM.SystemLinearEquations_ZeroAllVectors(self._heatSLE._cself, None)
libUnderworld.StgFEM.SystemLinearEquations_MatrixSetup(self._heatSLE._cself, None)
libUnderworld.StgFEM.SystemLinearEquations_VectorSetup(self._heatSLE._cself, None)
if nonLinear and nonLinearIterate:
libUnderworld.StgFEM.SystemLinearEquations_NonLinearExecute(self._heatSLE._cself, None)
else:
libUnderworld.StgFEM.SystemLinearEquations_ExecuteSolver(self._heatSLE._cself, None)
libUnderworld.StgFEM.SystemLinearEquations_UpdateSolutionOntoNodes(self._heatSLE._cself, None)
if isinstance(self._heatSLE, (uw.systems.SteadyStateDarcyFlow)):
self._heatSLE.solve_velocityField()
########################################################################
### setup options for solve
########################################################################
def _setup_options(self, **kwargs):
self._optionsStr=''
for key, value in self.options.EnergySolver.__dict__.items():
self._optionsStr += " "+"-EnergySolver_"+key+" "+str(value)
for key, value in kwargs.items(): # kwargs is a regular dictionary
self._optionsStr += " "+"-"+key+" "+str(value)
def _check_linearity(self, nonLinearIterate):
nonLinear = False
# can only check for the heatEq SLE
if isinstance(self._heatSLE, uw.systems.SteadyStateHeat):
sle = self._heatSLE
message = "Nonlinearity detected."
if sle._temperatureField in sle.fn_diffusivity._underlyingDataItems:
nonLinear = True
message += "\nDiffusivity function depends on the temperature field provided to the system."
if sle._temperatureField in sle.fn_heating._underlyingDataItems:
nonLinear = True
message += "\nHeating function depends on the temperature field provided to the system."
message += "\nPlease set the 'nonLinearIterate' solve parameter to 'True' or 'False' to continue."
if nonLinear and (nonLinearIterate==None):
raise RuntimeError(message)
return nonLinear
class OptionsGroup(object):
"""
EnergySolver : KSP solver options
"""
def __init__(self):
self.EnergySolver=Options()