Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Jerome Kieffer 2019-05-16 16:59:35 +02:00
commit 3ca98bf762
13 changed files with 324 additions and 70 deletions

View File

@ -3,7 +3,7 @@ pyFAI: Fast Azimuthal Integration in Python
Main development website: https://github.com/silx-kit/pyFAI
|Build Status| |Appveyor Status|
|Build Status| |Appveyor Status| |myBinder Launcher|
pyFAI is an azimuthal integration library that tries to be fast (as fast as C
and even more using OpenCL and GPU).
@ -122,15 +122,15 @@ Python 2.7, 3.4, 3.5, 3.6 and 3.7 are well tested.
Python 2.6, 3.2 and 3.3 are no more supported since pyFAI 0.12
For full functionality of pyFAI the following modules need to be installed.
* numpy - http://www.numpy.org
* scipy - http://www.scipy.org
* matplotlib - http://matplotlib.sourceforge.net/
* fabio - http://sourceforge.net/projects/fable/files/fabio/
* h5py - http://www.h5py.org/
* pyopencl - http://mathema.tician.de/software/pyopencl/
* python-pyqt5 - http://www.riverbankcomputing.co.uk/software/pyqt/intro
* silx - http://www.silx.org
* numexpr - https://github.com/pydata/numexpr
* ``numpy`` - http://www.numpy.org
* ``scipy`` - http://www.scipy.org
* ``matplotlib`` - http://matplotlib.sourceforge.net/
* ``fabio`` - http://sourceforge.net/projects/fable/files/fabio/
* ``h5py`` - http://www.h5py.org/
* ``pyopencl`` - http://mathema.tician.de/software/pyopencl/
* ``pyqt5`` - http://www.riverbankcomputing.co.uk/software/pyqt/intro
* ``silx`` - http://www.silx.org
* ``numexpr`` - https://github.com/pydata/numexpr
Those dependencies can simply be installed by::
@ -149,15 +149,15 @@ or using apt-get on from the command line in a terminal::
The extra Ubuntu packages needed are:
* python-numpy
* python-scipy
* python-matplotlib
* python-dev
* python-fabio
* python-pyopencl
* python-pyqt5
* python-silx
* python-numexpr
* ``python-numpy``
* ``python-scipy``
* ``python-matplotlib``
* ``python-dev``
* ``python-fabio``
* ``python-pyopencl``
* ``python-pyqt5``
* ``python-silx``
* ``python-numexpr``
and the same with python3
using apt-get these can be installed as::
@ -203,34 +203,36 @@ One needs to subscribe by sending an email to sympa@esrf.fr with a subject "subs
Maintainers
-----------
* Jérôme Kieffer (ESRF)
* Valentin Valls (ESRF)
* Jérôme Kieffer (ESRF)
* Valentin Valls (ESRF)
Contributors
------------
* Frédéric-Emmanuel Picca (Soleil)
* Thomas Vincent (ESRF)
* Dimitris Karkoulis (ESRF)
* Aurore Deschildre (ESRF)
* Giannis Ashiotis (ESRF)
* Zubair Nawaz (Sesame)
* Jon Wright (ESRF)
* Amund Hov (ESRF)
* Dodogerstlin @github
* Gunthard Benecke (Desy)
* Gero Flucke (Desy)
* Frédéric-Emmanuel Picca (Soleil)
* Thomas Vincent (ESRF)
* Dimitris Karkoulis (ESRF)
* Aurore Deschildre (ESRF)
* Giannis Ashiotis (ESRF)
* Zubair Nawaz (Sesame)
* Jon Wright (ESRF)
* Amund Hov (ESRF)
* Dodogerstlin @github
* Gunthard Benecke (Desy)
* Gero Flucke (Desy)
Indirect contributors (ideas...)
--------------------------------
* Peter Boesecke
* Manuel Sánchez del Río
* Vicente Armando Solé
* Brian Pauw
* Veijo Honkimaki
* Peter Boesecke
* Manuel Sánchez del Río
* Vicente Armando Solé
* Brian Pauw
* Veijo Honkimaki
.. |Build Status| image:: https://travis-ci.org/silx-kit/pyFAI.svg?branch=master
:target: https://travis-ci.org/silx-kit/pyFAI
.. |Appveyor Status| image:: https://ci.appveyor.com/api/projects/status/github/silx-kit/pyfai?svg=true
:target: https://ci.appveyor.com/project/ESRF/pyfai
.. |myBinder Launcher| image:: https://mybinder.org/badge_logo.svg
:target: https://mybinder.org/v2/gh/silx-kit/pyFAI/master?filepath=binder%2Findex.ipynb

82
binder/index.ipynb Normal file
View File

@ -0,0 +1,82 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# [pyFAI](https://github.com/silx-kit/pyFAI) cookbooks and tutorials\n",
"\n",
"See the [full documentation](http://www.silx.org/doc/pyFAI/latest/) for more information."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Cookbooks\n",
"\n",
"- [Integration with python](../doc/source/usage/cookbook/integration_with_python.ipynb)\n",
"- [Integration with scripts](../doc/source/usage/cookbook/integration_with_scripts.ipynb)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Tutorials\n",
"\n",
"- [Introduction](../doc/source/usage/tutorial/Introduction/introduction.ipynb)\n",
"- [CCD calibration](../doc/source/usage/tutorial/CCD_Calibration/CCD_calibration.ipynb)\n",
"- [Calibrant](../doc/source/usage/tutorial/Calibrant/Calibrant.ipynb)\n",
"- [New calibrant](../doc/source/usage/tutorial/Calibrant/new_calibrant.ipynb)\n",
"- [Distortion](../doc/source/usage/tutorial/Distortion/Distortion.ipynb)\n",
"- [Geometry](../doc/source/usage/tutorial/Geometry/geometry.ipynb)\n",
"- Goniometer:\n",
"\n",
" - [Rotation-Pilatus100k](../doc/source/usage/tutorial/Goniometer/Rotation-Pilatus100k/Multi120_Pilatus100k.ipynb)\n",
" - [Rotation-XPADS540](../doc/source/usage/tutorial/Goniometer/Rotation-XPADS540/D2AM-15.ipynb)\n",
" - [Translation-Pilatus6M](../doc/source/usage/tutorial/Goniometer/Translation-Pilatus6M/TTcalibration.ipynb)\n",
" \n",
"- [Inpainting](../doc/source/usage/tutorial/Inpainting/Inpainting.ipynb)\n",
"- [LogScale](../doc/source/usage/tutorial/LogScale/Guinier.ipynb)\n",
"- [MakeCalibrant](../doc/source/usage/tutorial/MakeCalibrant/make_calibrant.ipynb)\n",
"- [MultiGeometry](../doc/source/usage/tutorial/MultiGeometry/MultiGeometry.ipynb)\n",
"- [Ellipse](../doc/source/usage/tutorial/Ellipse/ellipse.ipynb)\n",
"- Soleil:\n",
"\n",
" - [Cristal_Mythen](../doc/source/usage/tutorial/Soleil/Cristal_Mythen.ipynb)\n",
" - [Diffabs_Calibration_K6C](../doc/source/usage/tutorial/Soleil/Soleil_Diffabs_Calibration_K6C.ipynb)\n",
" - [Diffabs_Diffraction_Tomography](../doc/source/usage/tutorial/Soleil/Soleil_Diffabs_Diffraction_Tomography.ipynb)\n",
"\n",
"- Thick Detector:\n",
"\n",
" - [Deconvolution](../doc/source/usage/tutorial/ThickDetector/deconvolution.ipynb)\n",
" - [Goniometer id28](../doc/source/usage/tutorial/ThickDetector/goniometer_id28.ipynb)\n",
" - [Raytracing](../doc/source/usage/tutorial/ThickDetector/raytracing.ipynb)\n",
"\n",
"- [Variance](../doc/source/usage/tutorial/Variance/Variance.ipynb)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.4.2"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

19
binder/postBuild Normal file
View File

@ -0,0 +1,19 @@
#!/bin/bash
# Setup jupyter bash kernel
python -m bash_kernel.install
# Download resources
RESOURCES_URL="http://www.silx.org/pub/pyFAI"
echo "Download notebooks from ${RESOURCES_URL}"
COOKBOOK_DIR="doc/source/usage/cookbook"
COOKBOOK_FILES="LaB6_29.4keV.tif
LaB6_29.4keV.poni
F_K4320T_Cam43_30012013_distorsion.spline"
for FILE in ${COOKBOOK_FILES}; do
URL="${RESOURCES_URL}/cookbook/calibration/${FILE}";
echo "Download ${URL}";
wget "${URL}" -O ${COOKBOOK_DIR}/${FILE};
done

3
binder/requirements.txt Normal file
View File

@ -0,0 +1,3 @@
pyFAI
ipympl
bash_kernel

View File

@ -1,13 +1,36 @@
:Author: Jérôme Kieffer
:Date: 19/12/2018
:Date: 16/05/2019
:Keywords: changelog
ChangeLog of Versions
=====================
0.18.0 15/05/2019
-----------------
* Last release with Valentin as he finishes his contract soon
* almost 800 commits, 60 PR since the last release: this is a huge release !
* Major rework on all GUIs, mainly pyFAI-calib2 and pyFAI-integrate.
* Possibility to integrate image stacks (i.e. from HDF5), ...
* Rework the *method* to specify the algorithm, pixel splitting and implementation
with sensible fall-backs. Also available via the different GUIs
* 3D visualization of detectors and experimental setup, useful for non flat detectors.
* `integrate1d_ng` is available with histogramming without pixel splitting in
Python, Cython and OpenCL. Now, propagates the variance properly !
* IO sub-packages with associated refactoring for ponifile, json, ...
* Improved management of OpenMP: simplify the code for histograms.
* Improved geometry description and tutorial for writing exchange with other
software (ImageD11, thanks to Carsten Detlefs).
* More reliable simple ellipse fitting with tests and doc.
* Better POCL integration (debugged on cuda, x87, Power9, ...)
* Rely on *silx* mechanics for the build, test, download, GUI, opencl ...
* Many new tutorials, now available on binder-hub: new calibrants, Pilatus calibration, ...
* Fix many issues reported in third-party software (Dioptas, ...)
* Drop support of debian8, Python 2.7 and Python 3.4 on all platforms.
It is likely most functionalities still work but without guaranty.
0.17.0 19/12/2018
-----------------
* Only 200 commits in a couple of month, this ia a small release
* Only 200 commits in a couple of month, this is a small release
* Fix major bugs in pyFAI-calib2 (double validator, initial guess, ring position)
* Constrains have been added to the geometry fitting of pyFAI-calib2
* New pyFAI-integrate graphical application

View File

@ -35,7 +35,7 @@ __author__ = "Jérôme Kieffer"
__contact__ = "Jerome.Kieffer@ESRF.eu"
__license__ = "MIT"
__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France"
__date__ = "01/03/2019"
__date__ = "16/05/2019"
__status__ = "stable"
@ -1111,13 +1111,15 @@ class NexusDetector(Detector):
setattr(cloned, name, value)
return cloned
def __deepcopy__(self):
def __deepcopy__(self, memo=None):
import copy
cloned = self.__class__()
if memo is not None:
memo[id(self)] = cloned
for name in self._ATTRIBUTES_TO_CLONE:
if hasattr(self, name):
value = getattr(self, name)
value = copy.deepcopy(value)
value = copy.deepcopy(value, memo)
setattr(cloned, name, value)
return cloned

View File

@ -42,7 +42,7 @@ __author__ = "Jerome Kieffer"
__contact__ = "Jerome.Kieffer@ESRF.eu"
__license__ = "MIT"
__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France"
__date__ = "09/05/2019"
__date__ = "16/05/2019"
__status__ = "production"
__docformat__ = 'restructuredtext'
@ -1439,6 +1439,91 @@ class Geometry(object):
logger.warning("Rotation conversion from pyFAI to SPD is not yet implemented")
return res
def getImageD11(self):
"""Export the current geometry in ImageD11 format.
Please refer to the documentation in doc/source/geometry_conversion.rst
for the orientation and units of those values.
:return: an Ordered dict with those parameters:
distance 294662.658 #in nm
o11 1
o12 0
o21 0
o22 -1
tilt_x 0.00000
tilt_y -0.013173
tilt_z 0.002378
wavelength 0.154
y-center 1016.328171
y-size 48.0815
z-center 984.924425
z-size 46.77648
"""
f2d = self.getFit2D()
distance = f2d.get("directDist", 0) * 1e3 # mm -> µm
y_center = f2d.get("centerX", 0) # in pixel
z_center = f2d.get("centerY", 0) # in pixel
tilt_x = self.rot3
tilt_y = self.rot2
tilt_z = -self.rot1
out = OrderedDict([("distance", distance),
("o11", 1),
("o12", 0),
("o21", 0),
("o22", -1),
("tilt_x", tilt_x),
("tilt_y", tilt_y),
("tilt_z", tilt_z),
])
if self._wavelength:
out["wavelength"] = self.wavelength * 1e9 # nm
if y_center:
out["y-center"] = y_center
out["y-size"] = self.detector.pixel2 * 1e6 # µm
if z_center:
out["z-center"] = z_center
out["z-size"] = self.detector.pixel1 * 1e6 # µm
return out
def setImageD11(self, param):
"""Set the geometry from the parameter set which contains distance,
o11, o12, o21, o22, tilt_x, tilt_y tilt_z, wavelength, y-center, y-size,
z-center and z-size.
Please refer to the documentation in doc/source/geometry_conversion.rst
for the orientation and units of those values.
:param param: dict with the values to set.
"""
o11 = param.get("o11")
if o11 is not None:
assert o11 == 1, "Only canonical orientation is supported"
o12 = param.get("o12")
if o12 is not None:
assert o12 == 0, "Only canonical orientation is supported"
o21 = param.get("o21")
if o21 is not None:
assert o21 == 0, "Only canonical orientation is supported"
o22 = param.get("o22")
if o22 is not None:
assert o22 == -1, "Only canonical orientation is supported"
self.rot3 = param.get("tilt_x", 0.0)
self.rot2 = param.get("tilt_y", 0.0)
self.rot1 = -param.get("tilt_z", 0.0)
distance = param.get("distance", 0.0) * 1e-6 # ->m
self.dist = distance * cos(self.rot2) * cos(self.rot1)
pixel_v = param.get("z-size", 0.0) * 1e-6
pixel_h = param.get("y-size", 0.0) * 1e-6
self.poni1 = -distance * sin(self.rot2) + pixel_v * param.get("z-center", 0.0)
self.poni2 = +distance * cos(self.rot2) * sin(self.rot1) + pixel_h * param.get("y-center", 0.0)
self.detector = detectors.Detector(pixel1=pixel_v, pixel2=pixel_h)
wl = param.get("wavelength")
if wl:
self.wavelength = wl * 1e-9
self.reset()
return self
def set_param(self, param):
"""set the geometry from a 6-tuple with dist, poni1, poni2, rot1, rot2,
rot3

View File

@ -27,7 +27,7 @@ from __future__ import absolute_import
__authors__ = ["V. Valls"]
__license__ = "MIT"
__date__ = "03/01/2019"
__date__ = "14/05/2019"
import logging
import functools
@ -91,9 +91,10 @@ class MarkerManager(object):
_logger.debug("Backtrace", exc_info=True)
self.__directDist = None
if geometry is not None:
invertGeometry = InvertGeometry(
self.__geometry.array_from_unit(typ="center", unit=pyFAI.units.TTH_RAD, scale=False),
self.__geometry.chiArray())
geometry.array_from_unit(typ="center", unit=pyFAI.units.TTH_RAD, scale=False),
geometry.chiArray())
self.__markerModel.lockSignals()
for marker in self.__markerModel:
@ -102,12 +103,12 @@ class MarkerManager(object):
chiRad, tthRad = marker.physicalPosition()
pixel = None
if self.__geometry is not None:
if geometry is not None:
pixel = invertGeometry(tthRad, chiRad, True)
ax, ay = numpy.array([pixel[1]]), numpy.array([pixel[0]])
tth = self.__geometry.tth(ay, ax)[0]
chi = self.__geometry.chi(ay, ax)[0]
tth = geometry.tth(ay, ax)[0]
chi = geometry.chi(ay, ax)[0]
error = numpy.sqrt((tthRad - tth) ** 2 + (chiRad - chi) ** 2)
if error > 0.05:

View File

@ -115,9 +115,9 @@ class CalibrationState(qt.QObject):
def __init__(self, parent):
qt.QObject.__init__(self, parent)
self.__reset()
self.reset()
def __reset(self):
def reset(self):
self.__geoRef = None
self.__geometry = None
self.__rings = None
@ -157,7 +157,7 @@ class CalibrationState(qt.QObject):
"""Invalidate the object and remove the ownershit of the geometry
refinment"""
geoRef = self.__geoRef
self.__reset()
self.reset()
return geoRef
def update(self, calibration):
@ -909,15 +909,19 @@ class GeometryTask(AbstractCalibrationTask):
def __geometryUpdated(self):
calibration = self.__getCalibration()
if calibration is None:
self.__calibrationState.reset()
return
if not calibration.isValid():
self.__showDialogCalibrationDiverge()
self.__calibrationState.reset()
return
geometry = self.model().fittedGeometry()
if geometry.isValid():
resetResidual = self.__fitting is not True
calibration.fromGeometryModel(geometry, resetResidual=resetResidual)
self.__calibrationState.update(calibration)
else:
self.__calibrationState.reset()
geoRef = calibration.getPyfaiGeometry()
self.__plot.markerManager().updatePhysicalMarkerPixels(geoRef)

View File

@ -27,7 +27,7 @@ from __future__ import absolute_import
__authors__ = ["V. Valls"]
__license__ = "MIT"
__date__ = "07/05/2019"
__date__ = "15/05/2019"
import logging
import numpy
@ -378,19 +378,31 @@ class _PeakPickingPlot(silx.gui.plot.PlotWidget):
if hasattr(self, "centralWidget"):
self.centralWidget().installEventFilter(self)
def setInteractiveMode(self, mode, color='black',
shape='polygon', label=None,
zoomOnWheel=True, source=None, width=None):
"""Override the function to allow to disable extrat interaction modes.
"""
self.setPeakInteractiveMode(self.PEAK_SELECTION_MODE)
silx.gui.plot.PlotWidget.setInteractiveMode(self, mode, color=color, shape=shape, label=label, zoomOnWheel=zoomOnWheel, source=source, width=width)
def peakInteractiveMode(self):
"""Returns the peak interactive mode selected."""
return self.__mode
def setPeakInteractiveMode(self, mode):
if self.__mode == mode:
return
self.__mode = mode
if mode == self.PEAK_SELECTION_MODE:
self.setInteractiveMode('zoom')
super(_PeakPickingPlot, self).setInteractiveMode('zoom')
elif mode == self.ERASOR_MODE:
color = "black"
self.setInteractiveMode('draw', shape='rectangle', source=self, color=color)
super(_PeakPickingPlot, self).setInteractiveMode('draw', shape='rectangle', source=self, color=color)
elif mode == self.BRUSH_MODE:
color = "black"
self.setInteractiveMode('draw', shape='rectangle', source=self, color=color)
super(_PeakPickingPlot, self).setInteractiveMode('draw', shape='rectangle', source=self, color=color)
else:
assert(False)
@ -1077,8 +1089,6 @@ class PeakPickingTask(AbstractCalibrationTask):
self.addAction(action)
def __onPlotModeChanged(self, owner):
if owner is None:
return
# TODO: This condition should not be reached like that
if owner is not self.__plot:
# Here a default plot tool is triggered

View File

@ -34,16 +34,14 @@ __authors__ = ["Jérôme Kieffer"]
__contact__ = "jerome.kieffer@esrf.eu"
__license__ = "MIT"
__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France"
__date__ = "01/03/2019"
__date__ = "16/05/2019"
import sys
import os
import unittest
from . import utilstest
# not importing test_all here to prevent premature initialization of UtilsTest
# and preventing the test skipping
from pyFAI.test.utilstest import test_options
# Issue https://github.com/silx-kit/fabio/pull/291
@ -59,6 +57,8 @@ if fabio.hdf5image.Hdf5Image.close.__module__ != "fabio.hdf5image":
def suite():
# Importing locally to prevent premature initialization of UtilsTest
# and preventing the test skipping
from . import test_all
test_suite = unittest.TestSuite()
test_suite.addTest(test_all.suite())
@ -67,6 +67,7 @@ def suite():
def run_tests():
"""Run test complete test_suite"""
test_options.configure()
runner = unittest.TextTestRunner()
if not runner.run(suite()).wasSuccessful():
print("Test suite failed")

View File

@ -34,7 +34,7 @@ __author__ = "Jérôme Kieffer"
__contact__ = "Jerome.Kieffer@ESRF.eu"
__license__ = "MIT"
__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France"
__date__ = "10/01/2018"
__date__ = "16/05/2019"
import unittest
@ -100,6 +100,21 @@ class TestFIT2D(unittest.TestCase):
res = testExport(tilt=20, tpr=580)
self.assertFalse(res, res)
def test_ImageD11(self):
ai = AzimuthalIntegrator()
ai.setFit2D(100, centerX=900, centerY=1000, tilt=20, tiltPlanRotation=80, pixelX=50, pixelY=60)
ai.wavelength = 1.234e-10
param = ai.getImageD11()
ai2 = AzimuthalIntegrator()
ai2.setImageD11(param)
for key in ["dist", "poni1", "poni2", "rot1", "rot2", "rot3", "pixel1", "pixel2", "splineFile", "wavelength"]:
refv = ai.__getattribute__(key)
obtv = ai2.__getattribute__(key)
if refv is None:
self.assertEqual(refv, obtv, "%s: %s != %s" % (key, refv, obtv))
else:
self.assertAlmostEqual(refv, obtv, 4, "%s: %s != %s" % (key, refv, obtv))
class TestSPD(unittest.TestCase):
poniFile = "Pilatus1M.poni"

View File

@ -29,7 +29,7 @@ __author__ = "Jérôme Kieffer"
__contact__ = "jerome.kieffer@esrf.eu"
__license__ = "MIT"
__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France"
__date__ = "12/02/2019"
__date__ = "16/05/2019"
PACKAGE = "pyFAI"
@ -133,11 +133,11 @@ class TestOptions(object):
"""
return
def configure(self, parsed_options):
def configure(self, parsed_options=None):
"""Configure the TestOptions class from the command line arguments and the
environment variables
"""
if not parsed_options.gui:
if parsed_options is not None and not parsed_options.gui:
self.WITH_QT_TEST = False
self.WITH_QT_TEST_REASON = "Skipped by command line"
elif os.environ.get('WITH_QT_TEST', 'True') == 'False':
@ -147,20 +147,27 @@ class TestOptions(object):
self.WITH_QT_TEST = False
self.WITH_QT_TEST_REASON = "DISPLAY env variable not set"
if not parsed_options.opencl or os.environ.get('PYFAI_OPENCL', 'True') == 'False':
if parsed_options is not None and not parsed_options.opencl:
self.WITH_OPENCL_TEST = False
# That's an easy way to skip OpenCL tests
# It disable the use of OpenCL on the full silx project
os.environ['PYFAI_OPENCL'] = "False"
elif os.environ.get('PYFAI_OPENCL', 'True') == 'False':
self.WITH_OPENCL_TEST = False
# That's an easy way to skip OpenCL tests
# It disable the use of OpenCL on the full silx project
os.environ['PYFAI_OPENCL'] = "False"
if not parsed_options.opengl:
if parsed_options is not None and not parsed_options.opengl:
self.WITH_GL_TEST = False
self.WITH_GL_TEST_REASON = "Skipped by command line"
elif os.environ.get('WITH_GL_TEST', 'True') == 'False':
self.WITH_GL_TEST = False
self.WITH_GL_TEST_REASON = "Skipped by WITH_GL_TEST env var"
if parsed_options.low_mem or os.environ.get('PYFAI_LOW_MEM', 'True') == 'False':
if parsed_options is not None and parsed_options.low_mem:
self.TEST_LOW_MEM = True
elif os.environ.get('PYFAI_LOW_MEM', 'True') == 'False':
self.TEST_LOW_MEM = True
def add_parser_argument(self, parser):