Source code for slippy.contact.models
"""
model object, just a container for step object that do the real work
"""
import os
import typing
import slippy
import warnings
from collections import OrderedDict
from contextlib import redirect_stdout, ExitStack
from datetime import datetime
from slippy.core.outputs import OutputSaver, OutputRequest
from slippy.core import _SurfaceABC, _LubricantModelABC, _ContactModelABC, _AdhesionModelABC
from slippy.contact.steps import _ModelStep, InitialStep
__all__ = ['ContactModel']
[docs]class ContactModel(_ContactModelABC):
""" A container for multi step contact mechanics and lubrication problems
Parameters
----------
name: str
The name of the contact model, used for output and log files by default
surface_1, surface_2: _SurfaceABC
A surface object with the height profile and the material for the surface set. The first surface will be the
master surface, when grid points are not aligned surface 2 will be interpolated on the grid points for
surface 1.
lubricant: _LubricantModelABC, optional (None)
A lubricant model
adhesion: _AdhesionModelABC, optional (None)
An adhesion model
output_dir: str, optional (None)
Path to an output directory can be relative or absolute, slippy will attempt to make directory if it does not
exist, defaults to slippy.OUTPUT_DIR, which defaults to the current working directory.
Attributes
----------
steps: OrderedDict
The model steps in the order they will be solved in, each value will be a ModelStep object.
surface_1, surface_2: _SurfaceABC
Surface objects of the two model surfaces
lubricant_model: _LubricantModelABC
A lubricant model
Methods
-------
add_step
Adds a step object to the model
add_output
Adds an output request to the model or selected steps
data_check
Performs analysis checks for each of the steps and the model as a whole, prints the results to the log file
solve
Solves all of the model steps in sequence, writing history and field outputs to the output file. Writes progress
to the log file
"""
"""Flag set to true if one of the surfaces is rigid"""
_is_rigid: bool = False
steps: OrderedDict
_current_state_debug: dict = None
_all_step_outputs = []
[docs] def __init__(self, name: str, surface_1: _SurfaceABC, surface_2: _SurfaceABC = None,
lubricant: _LubricantModelABC = None, adhesion: _AdhesionModelABC = None, output_dir: str = None):
self.surface_1 = surface_1
self.surface_2 = surface_2
self.name = name
self.lubricant_model = lubricant
self.adhesion = adhesion
if adhesion is not None and lubricant is not None:
warnings.warn("No steps can handle both adhesion and lubrication at this time")
self.steps = OrderedDict({'Initial': InitialStep()})
self.current_step = None
self.current_step_start_time = None
if output_dir is not None:
if not os.path.isdir(output_dir):
try:
os.mkdir(output_dir)
except OSError:
raise ValueError("Output directory not found and creation of the output "
"directory: %s failed" % output_dir)
slippy.OUTPUT_DIR = output_dir
@property
def log_file_name(self):
return os.path.join(slippy.OUTPUT_DIR, self.name + '.log')
[docs] def add_step(self, step_instance: _ModelStep = None, position: typing.Union[int, str] = None):
""" Adds a solution step to the current model
Parameters
----------
step_instance: _ModelStep
An instance of a model step
position : {int, None}, optional (None)
The position of the step in the existing order, if None will add step to the end
See Also
--------
step
Notes
-----
Steps should only be added to the model using this method
#TODO detailed description of inputs
Examples
--------
>>> #TODO
"""
new_step = step_instance
step_name = step_instance.name
step_instance.model = self
if position is None:
self.steps[step_name] = new_step
else:
keys = list(self.steps.keys())
values = list(self.steps.values())
if type(position) is str:
position = keys.index(position)
keys.insert(position, step_name)
values.insert(position, new_step)
self.steps = OrderedDict()
for k, v in zip(keys, values):
self.steps[k] = v
for sub_model in step_instance.sub_models:
sub_model.model = self
[docs] def add_output(self, output_request: OutputRequest, active_steps: typing.Union[str, typing.Sequence[str]] = 'all'):
"""
Add an output to one or more steps in the model
Parameters
----------
output_request: OutputRequest
A slippy.contact.OutputRequest with describing the parameters to save and the time points they will be saved
for
active_steps: str or list of str, optional ('all')
The step or a list of steps this output is active for, defaults to all the steps currently in the model
Examples
--------
"""
if active_steps == 'all':
active_steps = set(self.steps)
active_steps.remove('Initial')
if isinstance(active_steps, str):
active_steps = [active_steps, ]
for this_step in active_steps:
self.steps[this_step].outputs.append(output_request)
[docs] def data_check(self):
print("Data check started at:")
print(datetime.now().strftime('%H:%M:%S %d-%m-%Y'))
print(f"Checking model {self.name}:")
self._model_check()
current_state = None
for this_step in self.steps:
current_state = self.steps[this_step]._data_check(current_state)
def _model_check(self):
"""
Checks the model for possible errors (the model steps are checked independently)
"""
def warn_or_error(msg):
if slippy.ERROR_IN_DATA_CHECK:
raise ValueError(msg)
else:
warnings.warn(msg)
if self.surface_1 is None:
warn_or_error("Master surface is not set")
if not self.surface_1.is_discrete:
warn_or_error("Master is not discrete, only secondary surface may be analytic")
if self.surface_1.material is None:
warn_or_error("Material for master surface is not set")
if self.surface_2 is None:
warn_or_error("Secondary surface is not set")
if self.surface_2.material is None:
warn_or_error("Material for second surface is not set, for a rigid surface use the Rigid material")
[docs] def solve(self, verbose: bool = False, skip_data_check: bool = False):
"""
Solve all steps and sub-models in the model as well as writing all outputs
Parameters
----------
verbose: bool optional (False)
If True, logs are written to the console instead of the log file
skip_data_check: bool, optional (False)
If True the data check will be skipped, this is not recommended but may be necessary for some steps
Returns
-------
current_state: dict
A dictionary containing the final state of the model
Notes
-----
Most steps produce detailed logging information that can be found in the log file.
"""
if os.path.exists(self.log_file_name):
os.remove(self.log_file_name)
current_state = {'time': 0.0}
with ExitStack() as stack:
output_writer = stack.enter_context(OutputSaver(self.name))
if not verbose:
log_file = stack.enter_context(open(self.log_file_name, 'x'))
stack.enter_context(redirect_stdout(log_file))
if not skip_data_check:
self.data_check()
print(f"Solving model {self.name}, CUDA = {slippy.CUDA}")
for this_step in self.steps:
print(f"Solving step {this_step}")
self.current_step = self.steps[this_step]
self.current_step_start_time = current_state['time']
for output in self.steps[this_step].outputs:
output.new_step(current_state['time'])
current_state = self.steps[this_step].solve(current_state, output_writer)
now = datetime.now().strftime('%H:%M:%S %d-%m-%Y')
print(f"Analysis completed successfully at: {now}")
return current_state
def __repr__(self):
return (f'ContactModel(surface1 = {repr(self.surface_1)}, '
f'surface2 = {repr(self.surface_2)}, '
f'steps = {repr(self.steps)})')
def __str__(self):
return (f'ContactModel with surfaces: {str(self.surface_1)}, {str(self.surface_2)}, '
f'and {len(self.steps)} steps: {", ".join([str(st) for st in self.steps])}')