Commit 5fc9aa0e3b3 by Pierre Augier

Cleanup many things (but not the subpackage objects)

parent 22c46e590cd
0.0.1a
------
- Split the package fluiddyn between one base package and specialized
packages.
0.1.0
-----
- Change class hierarchy, so that interfaces are no longer under instruments.
The new hierarchy highlights the separation between interface classes which
implement communication with boards (e.g. VISA library or Linux-GPIB library
to communicate with GPIB boards, socket to communicate with network devices,
etc.), and instrument classes (both abstract and concrete) which represent
specific devices. Some device may be connected in different manners, and
some physical boards can be addressed with various libraries.
The new hierarchy highlights the separation between interface classes which
implement communication with boards (e.g. VISA library or Linux-GPIB library to
communicate with GPIB boards, socket to communicate with network devices,
etc.), and instrument classes (both abstract and concrete) which represent
specific devices. Some device may be connected in different manners, and some
physical boards can be addressed with various libraries.
- Allow to use simple string description, e.g. VISA style string
("GPIB0::1::INSTR"), or IP address "192.168.0.4", in the __init__ method
of devices, instead of an Interface object. The most appropriate Interface
class will be infered from Instrument.default_interface class variable.
Default association can be changed with the fluidlab.interface.set_default_interface
function. In the future, permanent custom association could be provided with
a dedicated user configuration file.
("GPIB0::1::INSTR"), or IP address "192.168.0.4", in the __init__ method of
devices, instead of an Interface object. The most appropriate Interface class
will be infered from Instrument.default_interface class variable. Default
association can be changed with the fluidlab.interface.set_default_interface
function. In the future, permanent custom association could be provided with a
dedicated user configuration file.
- Change all interface and instrument `__init__` method to restrain from opening
communication. Indeed, one must not rely on the `__del__` method to close the
connection. Instead, the underlying interface must be opened and closed using the
`open()` and `close()` method, or a context manager must be used. The context manager
will open and close the underlying interface appropriately. The Interface abstract
base class maintains a boolean to retain the opened/closed state. Concreate Interface
class must only implement _open and _close methods without checking openness.
If some device requires initialisation communication, this should not be done in
`__init__`. Either use a dedicated instance method, and call it explicitely, or
overload the driver `__enter__`/`__exit__` methods.
- Change all interface and instrument `__init__` method to restrain from
opening communication. Indeed, one must not rely on the `__del__` method to
close the connection. Instead, the underlying interface must be opened and
closed using the `open()` and `close()` method, or a context manager must be
used. The context manager will open and close the underlying interface
appropriately. The Interface abstract base class maintains a boolean to retain
the opened/closed state. Concreate Interface class must only implement _open
and _close methods without checking openness. If some device requires
initialisation communication, this should not be done in `__init__`. Either use
a dedicated instance method, and call it explicitely, or overload the driver
`__enter__`/`__exit__` methods.
0.0.1a
------
- Split the package fluiddyn between one base package and specialized
packages.
......@@ -3,7 +3,7 @@
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = python $(shell which sphinx-build)
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = _build
......
......@@ -28,20 +28,9 @@ Modules Reference
fluidlab.instruments
fluidlab.objects
fluidlab.exp
fluidlab.postproc
fluidlab.daq
fluidlab.util
Scripts and examples
--------------------
FluidLab also comes with scripts and examples. They are organised in
the following directories:
.. autosummary::
:toctree: generated/
bin
scripts
More
----
......@@ -61,4 +50,3 @@ Indices and tables
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
......@@ -203,102 +203,6 @@
"source": [
"tank.profile.plot()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Representation of an experiment"
]
},
{
"cell_type": "raw",
"metadata": {},
"source": [
"Physically, an experiment consists in interacting objects. The\n",
"experimentalist wants to control the actions of the objects with a\n",
"good control in space and time and in a reproducible way. The results\n",
"are then measurements produced by the measuring objects. Usually,\n",
"after the experiment has been set up, it is repeated a number of times\n",
"in order to vary some parameters.\n",
"\n",
"A experimental set-up is represented in FluidDyn by a class derived\n",
"from the class :class:`fluidlab.exp.base.Experiment`. The\n",
"experiment class has attributes that represent the physical objects\n",
"interacting in the experimental set-up.\n",
"\n",
"Each realisation of the experimental set-up (with a particular set of\n",
"parameters) is represented by an instance of the experiment\n",
"class. Each experiment (each realisation) is associated with a\n",
"particular directory.\n",
"\n",
"In order to create a class, do for example:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from fluidlab.exp.taylorcouette.linearprofile import ILSTaylorCouetteExp\n",
"\n",
"exp = ILSTaylorCouetteExp(\n",
" rho_max=1.1, N0=1., prop_homog=0.1,\n",
" Omega1=0.4, Omega2=0, R1=150, R2=200,\n",
" description=\"\"\"This experiment is for the tutorial.\"\"\")\n",
"\n",
"[attr for attr in dir(exp) if not attr.startswith('_')]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print(exp.description)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print(exp.path_save)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"When this experiment has been created, the description files have been automatically saved in the \"right\" place. This \"right\" place being defined in the class of the experiment. Then we can easily reload the experiment."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"path_save = exp.path_save\n",
"del(exp)\n",
"exp = fluidlab.load_exp(path_save[-20:-5])\n",
"print(exp.path_save)\n",
"print('R2 = {}'.format(exp.params['R2']))"
]
},
{
"cell_type": "raw",
"metadata": {},
"source": [
"Note that I deliberately only use the string\n",
"``path_save[-20:-5]`` to show that\n",
":func:`fld.load_exp` is sufficiently clever to find out an experiment\n",
"that corresponds to this string. Be careful to provide a sufficiently\n",
"long string to be sure to load the wanted experiment."
]
}
],
"metadata": {
......@@ -317,7 +221,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.6.1"
"version": "3.7.2"
}
},
"nbformat": 4,
......
"""Sphinx extension: provide a new directive *mathmacro*.
This extension has to be added after the other math extension since it
redefined the math directive and the math role. For example, like this
(in the conf.py file)::
extensions = [
'sphinx.ext.autodoc', 'sphinx.ext.doctest',
'sphinx.ext.mathjax',
'sphinx.ext.viewcode', 'sphinx.ext.autosummary',
'numpydoc',
'mathmacro']
"""
from __future__ import print_function
import re
from docutils.parsers.rst.directives.misc import Replace
from sphinx.ext.mathbase import MathDirective
from sphinx.ext.mathbase import math_role
def multiple_replacer(replace_dict):
"""Return a function replacing doing multiple replacements.
The produced function replace `replace_dict.keys()` by
`replace_dict.values`, respectively.
"""
def replacement_function(match):
s = match.group(0)
end = s[-1]
if re.match(r'[\W_]', end):
return replace_dict[s[:-1]]+end
else:
return replace_dict[s]
pattern = "|".join([re.escape(k)+r'[\W_]'
for k in replace_dict.keys()])
pattern = re.compile(pattern, re.M)
return lambda string: pattern.sub(replacement_function, string)
def multiple_replace(string, replace_dict):
mreplace = multiple_replacer(replace_dict)
return mreplace(string)
class MathMacro(Replace):
"""Directive defining a math macro."""
def run(self):
if not hasattr(self.state.document, 'math_macros'):
self.state.document.math_macros = {}
latex_key = '\\'+self.state.parent.rawsource.split('|')[1]
self.state.document.math_macros[latex_key] = ''.join(self.content)
self.content[0] = ':math:`'+self.content[0]
self.content[-1] = self.content[-1]+'`'
return super().run()
class NewMathDirective(MathDirective):
"""New math block directive parsing the latex code."""
def run(self):
try:
math_macros = self.state.document.math_macros
except AttributeError:
pass
else:
if math_macros:
multiple_replace = multiple_replacer(math_macros)
for i, c in enumerate(self.content):
self.content[i] = multiple_replace(c)
for i, a in enumerate(self.arguments):
self.arguments[i] = multiple_replace(a)
return super().run()
def new_math_role(role, rawtext, text, lineno, inliner,
options={}, content=[]):
"""New math role parsing the latex code."""
try:
math_macros = inliner.document.math_macros
except AttributeError:
pass
else:
if math_macros:
rawtext = multiple_replace(rawtext, math_macros)
text = rawtext.split('`')[1]
return math_role(role, rawtext, text, lineno, inliner,
options=options, content=content)
def setup(app):
app.add_role('math', new_math_role)
app.add_directive('math', NewMathDirective)
app.add_directive('mathmacro', MathMacro)
"""
Laboratory experiments (:mod:`fluidlab`)
========================================
Laboratory experiments
======================
.. _lab:
.. currentmodule:: fluidlab
......@@ -12,7 +12,6 @@ The package :mod:`fluidlab` contains:
objects
exp
postproc
output
"""
......@@ -20,17 +19,3 @@ The package :mod:`fluidlab` contains:
from __future__ import print_function
from fluidlab._version import __version__
try:
from fluidlab.util import load_exp
import fluiddyn as fld
fld.load_exp = load_exp
del fld
except AttributeError:
# Some older Python versions do not have os.getppid() and
# therefore load_exp import fails.
# Not all setups require load_exp, therefore issue a warning
# here instead of raising an uncaught exception.
print("Warning: load_exp is not available.")
"""
Data acquisition boards (:mod:`fluidlab.instruments.daq`)
=========================================================
Data acquisition boards
=======================
.. todo:: Data acquisition board drivers.
......
......@@ -364,7 +364,7 @@ def write_analog(
Frequency rate for all channels (Hz).
volt_min: {number or iterable of numbers}
Minima for the channels
volt_max: {number or iterable of numbers}
......
......@@ -9,59 +9,6 @@
session
octavesession
.. warning::
Beware, most of the modules of this package (those listed below)
are depreciated and will be at least rewritten with other base
class.
Physically, an experiment consists in interacting objects. The
experimentalist wants to control the actions of the objects with a
good control in space and time and in a reproducible way. The results
are then some measurements about the studied physical phenomenon
produced by the measuring objects. Usually, after the experiment has
been set up, it is repeted a number of times in order to vary some
parameters.
A experimental set-up is represented in FluidDyn by a class derived
from the class :class:`fluidlab.exp.base.Experiment`. The
experiment class has attributes that represent the physical objects
interacting in the experimental set-up (composition).
Each realisation of the experimental set-up (with a particular set of
parameters) is represented by an instance of the experiment
class. Each experiment (each realisation) is associated with a
directory.
This package provides:
- some modules defining classes to represent base experiments:
.. autosummary::
:toctree:
base
withtank
withconductivityprobe
- a package with classes representing Taylor-Couette experiments:
.. autosummary::
:toctree:
taylorcouette
- other very simple classes derived from
:class:`fluidlab.exp.withtank.ExperimentWithTank` and
:class:`fluidlab.exp.withconductivityprobe.ExpWithConductivityProbe`,
respectively:
.. autosummary::
:toctree:
doublediffusion
vertduct
"""
from .session import Session
......
"""
Experiments on double diffusion (:mod:`fluidlab.exp.doublediffusion`)
=========================================================================
.. currentmodule:: fluidlab.exp.doublediffusion
Provides:
.. autoclass:: DoubleDiffusion
:members:
:private-members:
"""
from __future__ import division, print_function
from fluidlab.exp.withtank import ExperimentWithTank
class DoubleDiffusion(ExperimentWithTank):
"""Represent an experience on the double diffusion instability.
See the documentation of the inherited class.
"""
_base_dir = "Double_diffusion"
def __init__(
self, zs=None, rhos=None, params=None, description=None, str_path=None
):
# start the init. and guess if it is the first creation
self._init_from_str(str_path)
if self.first_creation:
# add a bit of description
description_base = """
Experiment in a small beaker on the double diffusion instability.
"""
description = self._complete_description(
description_base, description=description
)
# call the __init__ function of the inherited class
super().__init__(
rhos=rhos,
zs=zs,
params=params,
description=description,
str_path=str_path,
)
"""
Taylor-Couette experiments
==========================
.. currentmodule:: fluidlab.exp.taylorcouette
Provides:
.. autosummary::
:toctree:
base
linearprofile
quadprofile
twolayers
"""
from fluidlab.exp.taylorcouette.base import TaylorCouetteExp, A, c
"""
ILS TC experiments (:mod:`fluidlab.exp.taylorcouette.linearprofile`)
========================================================================
.. currentmodule:: fluidlab.exp.taylorcouette.linearprofile
Provides:
.. autoclass:: ILSTaylorCouetteExp
:members:
:private-members:
"""
from __future__ import division, print_function
import os
import numpy as np
from fluidlab.exp.taylorcouette import TaylorCouetteExp, A, c
from fluiddyn.util.constants import g, rho0, rho_pure_water
class ILSTaylorCouetteExp(TaylorCouetteExp):
"""Initially linear stratification Taylor-Couette experiment.
Parameters
----------
(for the __init__ method)
rho_min : number, optional
Density minimum (in kg/m^3).
rho_max : number, optional
Density maximum (in kg/m^3).
N0 : number, optional
Brunt-Vaisala frequency (in rad/s).
prop_homog : {0, number}, optional
Proportion of the height that is homogeneous.
Omega1 : number
Rotation rate of the inner cylinder (in rad/s).
Omega2 : {0., number}
Rotation rate of the outer cylinder (in rad/s).
R1 : number
Radius of the inner cylinder (in mm).
R2 : {261, number}
Radius of the outer cylinder (in mm).
params : dict, optional
Contain parameters. The previously listed parameters can be
given directly in this dictionary. Other parameters can be
added and will also be saved.
description : str, optional
A description of the experiment.
str_path : str, optional
A string related to the path where the experiment is saved
or will be saved.
Notes
-----
There are two modes of creating an instance of this class:
1. if `str_path` doesn't point on an already saved experiment, a
new experiment is created (the instance variable
`first_creation` is True). In this case, the parameters `rhos`
or `zs` have to be given (either directy or through the
dictionary `params`).
2. if `str_path` points on an already saved experiment, the instance
variable `first_creation` is False and the experiment is loaded.
Note that if you want to load an already saved experiment, it is
more convenient to use the function :func:`fluiddyn.load_exp` like
so::
exp = fld.load_exp('2014-03-25_12-43-48')
Attributes
----------
board : :class:`fluidlab.board.PowerDAQBoard`
For controlling the acquisition board.
sprobe : :class:`fluidlab.objects.probes.MovingConductivityProbe`
For controlling the conductivity probe and the traverse.
profiles : :class:`fluidlab.exp.withconductivityprobe.Profiles`
For profiles...
tank : :class:`fluidlab.objects.tanks.TaylorCouette`
Contains the informations on the tank and the density profile.
first_creation : bool
False if the experiment has not been loaded from the disk.
params : dict
Containing parameters.
description : str
A description of the experiment..
path_save : str
The absolute path of the directory associated with the experiment.
name_dir : str
Name of the directory associated with the experiment.
time_start : str
Coding the time of creation.
Examples
--------
Create an experiment::
exp = ILSTaylorCouetteExp(Omega1=Omega1, R1=R1,
rho_max=rho_max, N0=N0, prop_homog=prop_homog,
description='''The tank is slowly put into rotation with a
linear ramp during 1 hour to Omega_max = 0.75 rad/s.''')
Fill the tank::
str_path = 'Exp_Omega=0.75_N0=1.83_2014-05-16_09-20-32'
exp = fld.load_exp(str_path=str_path)
exp.tank.fill(pumps=True)
"""
_base_dir = os.path.join("TaylorCouette", "ILS")
def __init__(
self,
rho_min=None,
rho_max=None,
N0=None,
prop_homog=None,
Omega1=None,
Omega2=None,
R1=None,
R2=None,
description=None,
params=None,
str_path=None,
position_start=352.0,
position_max=None,
Deltaz=340.0,
need_board=True,
):
# start the init. and find out if it is the first creation
self._init_from_str(str_path)
if self.first_creation:
# add a bit of description
description_base = """
Initially linear stratification (ILS)...
"""
description = self._complete_description(
description_base, description=description
)
# initialise `params` with `params` and the other parameters
if params is None:
params = {}
if rho_min is None:
if "rho_min" in params:
rho_min = params["rho_min"]
else:
rho_min = rho_pure_water
params["rho_min"] = rho_min
if rho_max is not None:
params["rho_max"] = rho_max
if N0 is not None:
params["N0"] = N0
if prop_homog is not None:
params["prop_homog"] = prop_homog
if Omega1 is not None:
params["Omega1"] = Omega1
if Omega2 is not None:
params["Omega2"] = Omega2
if R1 is not None:
params["R1"] = R1
if R2 is not None:
params["R2"] = R2
# verify if enough params have been given for the first creation
self._verify_params_first_creation(
params,
keys_needed=[
"rho_min",
"rho_max",
"N0",
"prop_homog",
"Omega1",
"Omega2",
"R1",
"R2",
],
)
# calculate the parameters needed for the inherited class
Delta_rho = params["rho_max"] - params["rho_min"]
if prop_homog is None:
prop_homog = params["prop_homog"]
zs_norm = np.array([0, prop_homog / 2, 1 - prop_homog / 2, 1])
rhos_norm = np.array([1.0, 1.0, 0.0, 0.0])
if N0 is None:
N0 = params["N0"]
z_max = (
g * Delta_rho / (rho0 * N0 ** 2 * (1 - prop_homog)) * 1000
) # (mm)
zs = z_max * zs_norm
rhos = rho_min + Delta_rho * rhos_norm
params.update({"zs": zs, "rhos": rhos})
# call the __init__ function of the inherited class
super().__init__(
params=params,
description=description,
str_path=str_path,