Page MenuHomec4science

__main__.py
No OneTemporary

File Metadata

Created
Fri, Apr 19, 22:09

__main__.py

#!/usr/bin/env python3
# -*- mode: python; coding: utf-8 -*-
# vim: set ft=python:
#
# Copyright (©) 2016-2022 EPFL (École Polytechnique Fédérale de Lausanne),
# Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
"""Module entry point."""
import sys
import io
import time
import argparse
import tamaas as tm
import numpy as np
from types import SimpleNamespace
from pathlib import Path
__author__ = "Lucas Frérot"
__copyright__ = (
"Copyright (©) 2019-2022, EPFL (École Polytechnique Fédérale de Lausanne),"
"\nLaboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides)"
)
__license__ = "SPDX-License-Identifier: AGPL-3.0-or-later"
def load_stream(stream):
"""
Load numpy from binary stream (allows piping).
Code from
https://gist.github.com/CMCDragonkai/3c99fd4aabc8278b9e17f50494fcc30a
"""
np_magic = stream.read(6)
# use the sys.stdin.buffer to read binary data
np_data = stream.read()
# read it all into an io.BytesIO object
return io.BytesIO(np_magic + np_data)
def print_version():
"""Print version information."""
print(
f"""Tamaas {tm.__version__}
{tm.__copyright__}
Authors: {', '.join(tm.__author__)}
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."""
)
def make_surface(args):
"""Generate a surface."""
if args.generator == "random_phase":
generator = tm.SurfaceGeneratorRandomPhase2D(args.sizes)
elif args.generator == "filter":
generator = tm.SurfaceGeneratorFilter2D(args.sizes)
else:
raise ValueError(f"Unknown generator method {args.generator}")
generator.spectrum = tm.Isopowerlaw2D()
generator.spectrum.q0 = args.cutoffs[0]
generator.spectrum.q1 = args.cutoffs[1]
generator.spectrum.q2 = args.cutoffs[2]
generator.spectrum.hurst = args.hurst
if 'seed' in args.__dict__:
generator.random_seed = args.seed
surface = (
generator.buildSurface() / generator.spectrum.rmsSlopes() * args.rms
)
params = {
"q0": generator.spectrum.q0,
"q1": generator.spectrum.q1,
"q2": generator.spectrum.q2,
"hurst": generator.spectrum.hurst,
"random_seed": generator.random_seed,
"rms_slopes": args.rms,
"generator": args.generator,
}
return params, surface
def surface(args):
"""Print a surface to stdout."""
params, surface = make_surface(args)
output = args.output if args.output is not None else sys.stdout
try:
np.savetxt(output, surface, header=str(params))
except BrokenPipeError:
pass
def declarative(args):
"""Run simulation defined in YAML."""
from ruamel.yaml import YAML
tm.set_log_level(tm.LogLevel.error)
if not args.input:
input = sys.stdin
else:
input = args.input
yml = YAML(typ='safe')
data = yml.load(Path(input))
# Handle surface definition
if 'surface' not in data:
sys.exit("Error: simulation description does not declare a surface")
if len(data['surface']) > 1:
sys.exit("Error: expected only one surface definition, got multiple")
args = SimpleNamespace()
args.generator = list(data['surface'])[0]
for k, v in data['surface'][args.generator].items():
setattr(args, k, v)
try:
_, surface = make_surface(args)
except AttributeError as e:
attr = str(e).split(" ")[-1].replace("'", "")
sys.exit(f"Error: Surface property '{attr}' required but not specified")
# Handle model definition
if 'model' not in data:
sys.exit("Error: simulation description does not declare a model")
mdata = data['model']
try:
if mdata['type'] not in {'basic_1d', 'basic_2d'}:
sys.exit(f'Error: unrecognized model type \'{mdata["type"]}\'')
mtype = getattr(tm.model_type, mdata['type'])
model = tm.ModelFactory.createModel(
mtype, mdata['system_size'], mdata['shape'],
)
except KeyError as e:
sys.exit(f"Error: model property {e} required but not specified")
model.E = mdata.get('E', 1)
model.nu = mdata.get('nu', 0)
def contact(args):
"""Solve a contact problem."""
from tamaas.dumpers import NumpyDumper
tm.set_log_level(tm.LogLevel.error)
if not args.input:
input = sys.stdin
else:
input = args.input
surface = np.loadtxt(input)
discretization = surface.shape
system_size = [1.0, 1.0]
model = tm.ModelFactory.createModel(
tm.model_type.basic_2d, system_size, discretization
)
solver = tm.PolonskyKeerRey(model, surface, args.tol)
solver.solve(args.load)
dumper = NumpyDumper("numpy", "traction", "displacement")
dumper.dump_to_file(sys.stdout.buffer, model)
def plot(args):
"""Plot displacement and pressure maps."""
try:
import matplotlib.pyplot as plt
except ImportError:
print(
"Please install matplotlib to use the 'plot' command",
file=sys.stderr,
)
sys.exit(1)
fig, (ax_traction, ax_displacement) = plt.subplots(1, 2)
ax_traction.set_title("Traction")
ax_displacement.set_title("Displacement")
with load_stream(sys.stdin.buffer) as f_np:
data = np.load(f_np)
ax_traction.imshow(data["traction"])
ax_displacement.imshow(data["displacement"])
fig.set_size_inches(10, 6)
fig.tight_layout()
plt.show()
def main():
"""Process command line arguments."""
parser = argparse.ArgumentParser(
prog="tamaas",
description=(
"The tamaas command is a simple utility for surface"
" generation, contact computation and"
" plotting of contact solutions"
),
)
parser.add_argument(
"--version", action="store_true", help="print version info"
)
subs = parser.add_subparsers(
title="commands", description="utility commands"
)
# Arguments for surface command
parser_surface = subs.add_parser(
"surface", description="Generate a self-affine rough surface"
)
parser_surface.add_argument(
"--cutoffs",
"-K",
nargs=3,
type=int,
help="Long, rolloff, short wavelength cutoffs",
metavar=("k_l", "k_r", "k_s"),
required=True,
)
parser_surface.add_argument(
"--sizes",
nargs=2,
type=int,
help="Number of points",
metavar=("nx", "ny"),
required=True,
)
parser_surface.add_argument(
"--hurst", "-H", type=float, help="Hurst exponent", required=True
)
parser_surface.add_argument(
"--rms", type=float, help="Root-mean-square of slopes", default=1.0
)
parser_surface.add_argument(
"--seed", type=int, help="Random seed", default=int(time.time())
)
parser_surface.add_argument(
"--generator",
help="Generation method",
choices=("random_phase", "filter"),
default="random_phase",
)
parser_surface.add_argument(
"--output", "-o", help="Output file name (compressed if .gz)"
)
parser_surface.set_defaults(func=surface)
# Arguments for contact command
parser_contact = subs.add_parser(
"contact",
description="Compute the elastic contact solution with a given surface",
)
parser_contact.add_argument(
"--input", "-i", help="Rough surface file (default stdin)"
)
parser_contact.add_argument(
"--tol", type=float, default=1e-12, help="Solver tolerance"
)
parser_contact.add_argument(
"load", type=float, help="Applied average pressure"
)
parser_contact.set_defaults(func=contact)
# Arguments for plot command
parser_plot = subs.add_parser("plot", description="Plot contact solution")
parser_plot.set_defaults(func=plot)
# Arguments for declarative command
parser_decl = subs.add_parser(
"declarative",
description="Run simulation described by YAML file"
)
parser_decl.add_argument("input", help="YAML file")
parser_decl.set_defaults(func=declarative)
args = parser.parse_args()
if args.version:
print_version()
return
try:
args.func(args)
except AttributeError:
parser.print_usage()
if __name__ == "__main__":
main()

Event Timeline