diff --git a/.gitignore b/.gitignore index 3bfaed36..01219acf 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,4 @@ env* *.pyc .vscode docs/_build/ -docs/_autosummary/ \ No newline at end of file +docs/_autosummary/ diff --git a/LICENSE b/LICENSE index 561977c3..e1697828 100644 --- a/LICENSE +++ b/LICENSE @@ -657,4 +657,4 @@ the specific requirements. You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow -the GNU AGPL, see . \ No newline at end of file +the GNU AGPL, see . diff --git a/README.rst b/README.rst index 43634ddb..d746ec12 100644 --- a/README.rst +++ b/README.rst @@ -1,26 +1,50 @@ -**** Home -**** +======== -Open-pyFUS is a toolbox for planning and controlling focused ultrasound treatments. It generally replicates the functionality developed in the MATLAB `open-TFUS toolbox `_. +``openlifu`` is a toolbox for planning and controlling focused +ultrasound treatments. It generally replicates the functionality +developed in the MATLAB `open-TFUS +toolbox `__. Installation ------------ Requirements -^^^^^^^^^^^^ +~~~~~~~~~~~~ + Python 3.11 Create Virtual Environment -^^^^^^^^^^^^^^^^^^^^^^^^^^ -``cd open_pyfus`` -``C:\Users\\AppData\Local\Programs\Python\Python311\python.exe -m venv env`` +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Windows: + +.. code:: sh + + C:\Users\\AppData\Local\Programs\Python\Python311\python.exe -m venv env + .\env\Scripts\activate + +Linux: + +.. code:: sh + + python3.11 -m venv env Install project (editable) -^^^^^^^^^^^^^^^^^^^^^^^^^^ -``.\env\Scripts\activate`` -``pip install -e .`` +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +With this repo as the working directory: + +.. code:: sh + + pip install -e . Disclaimer ---------- -CAUTION - Investigational device. Limited by Federal (or United States) law to investigational use. The system described here has *not* been evaluated by the FDA and is not designed for the treatment or diagnosis of any disease. It is provided AS-IS, with no warranties. User assumes all liability and responsibility for identifying and mitigating risks associated with using this software. + +CAUTION - Investigational device. Limited by Federal (or United States) +law to investigational use. The system described here has *not* been +evaluated by the FDA and is not designed for the treatment or diagnosis +of any disease. It is provided AS-IS, with no warranties. User assumes +all liability and responsibility for identifying and mitigating risks +associated with using this software. diff --git a/__init__.py b/__init__.py deleted file mode 100644 index 8132b411..00000000 --- a/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from . import pyfus \ No newline at end of file diff --git a/docs/_static/.gitkeep b/docs/_static/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/docs/_templates/custom-class-template.rst b/docs/_templates/custom-class-template.rst index b8a16d85..f2905254 100644 --- a/docs/_templates/custom-class-template.rst +++ b/docs/_templates/custom-class-template.rst @@ -3,5 +3,5 @@ .. currentmodule:: {{ module }} .. autoclass:: {{ objname }} - :members: - :show-inheritance: \ No newline at end of file + :members: + :show-inheritance: diff --git a/docs/_templates/custom-module-template.rst b/docs/_templates/custom-module-template.rst index ef2c09a5..6adfe405 100644 --- a/docs/_templates/custom-module-template.rst +++ b/docs/_templates/custom-module-template.rst @@ -1,7 +1,7 @@ {{ fullname | escape | underline}} .. automodule:: {{ fullname }} - + {% block attributes %} {% if attributes %} .. rubric:: Module Attributes @@ -63,4 +63,4 @@ {{ item }} {%- endfor %} {% endif %} -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/docs/api.rst b/docs/api.rst index c5020eb8..c361f365 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -1,12 +1,11 @@ API Reference ============= -The top-level module containing most of the code is `pyfus` +The top-level module containing most of the code is ``openlifu`` -.. autosummary:: +.. autosummary:: :toctree: _autosummary :template: custom-module-template.rst :recursive: - pyfus - + openlifu diff --git a/docs/conf.py b/docs/conf.py index 63bfee90..5fffd216 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -6,7 +6,7 @@ # -- Project information ----------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information -project = 'Open-pyFUS' +project = 'OpenLIFU-Python' copyright = '2023, Openwater' author = 'Openwater' release = '0.1' diff --git a/docs/includeme.rst b/docs/includeme.rst index 6b2b3ec6..72a33558 100644 --- a/docs/includeme.rst +++ b/docs/includeme.rst @@ -1 +1 @@ -.. include:: ../README.rst \ No newline at end of file +.. include:: ../README.rst diff --git a/docs/index.rst b/docs/index.rst index 0281f922..7032bfbd 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,10 +1,10 @@ -Welcome to open-PYFUS's documentation! -========================================= +Welcome to openlifu's documentation! +========================================= .. toctree:: :maxdepth: 2 :caption: Contents: - + includeme api diff --git a/test_notebooks/foo.nii.gz b/notebooks/foo.nii.gz similarity index 100% rename from test_notebooks/foo.nii.gz rename to notebooks/foo.nii.gz diff --git a/test_notebooks/gridweights.nii.gz b/notebooks/gridweights.nii.gz similarity index 100% rename from test_notebooks/gridweights.nii.gz rename to notebooks/gridweights.nii.gz diff --git a/test_notebooks/intensity.nii.gz b/notebooks/intensity.nii.gz similarity index 100% rename from test_notebooks/intensity.nii.gz rename to notebooks/intensity.nii.gz diff --git a/test_notebooks/test_first.ipynb b/notebooks/test_first.ipynb similarity index 88% rename from test_notebooks/test_first.ipynb rename to notebooks/test_first.ipynb index f817cc55..e95e5103 100644 --- a/test_notebooks/test_first.ipynb +++ b/notebooks/test_first.ipynb @@ -8,7 +8,7 @@ "\n", "If you have my modified version of k-wave-python, install it with `pip install -e .` from the `k-wave-python` directory. If you don't have my modified version, you can install the original version with `pip install k-wave-python`. If you are using the original version, be sure to set `USE_GRIDWEIGHTS` to `False` in order to prevent `open_pyfus` from trying to use a nonexistent interface for loading the gridweights. \n", "\n", - "Also, if you are using the original version, import `pyfus` takes _way_ longer (45s on my PC), presumably hanging on `import kwave`. For some reason, it wants to re-download the binaries every time, even though they are already present in the the installation directory. I've opened an issue on this: https://github.com/waltsims/k-wave-python/issues/366." + "Also, if you are using the original version, import `openlifu` takes _way_ longer (45s on my PC), presumably hanging on `import kwave`. For some reason, it wants to re-download the binaries every time, even though they are already present in the the installation directory. I've opened an issue on this: https://github.com/waltsims/k-wave-python/issues/366." ] }, { @@ -28,7 +28,7 @@ "formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')\n", "handler.setFormatter(formatter)\n", "root.addHandler(handler)\n", - "import pyfus\n", + "import openlifu\n", "import numpy as np" ] }, @@ -55,7 +55,7 @@ "metadata": {}, "outputs": [], "source": [ - "arr = pyfus.Transducer.gen_matrix_array(nx=8, ny=8, pitch=4, kerf=.5, units=\"mm\", impulse_response=1e5)\n", + "arr = openlifu.Transducer.gen_matrix_array(nx=8, ny=8, pitch=4, kerf=.5, units=\"mm\", impulse_response=1e5)\n", "arr.draw()" ] }, @@ -72,11 +72,11 @@ "metadata": {}, "outputs": [], "source": [ - "pulse = pyfus.Pulse(frequency=400e3, duration=3/400e3)\n", - "sequence = pyfus.Sequence()\n", - "focal_pattern = pyfus.focal_patterns.Wheel(center=True, spoke_radius=5, num_spokes=5)\n", - "sim_setup = pyfus.SimSetup(dt=2e-7, t_end=100e-6)\n", - "protocol = pyfus.Protocol(\n", + "pulse = openlifu.Pulse(frequency=400e3, duration=3/400e3)\n", + "sequence = openlifu.Sequence()\n", + "focal_pattern = openlifu.focal_patterns.Wheel(center=True, spoke_radius=5, num_spokes=5)\n", + "sim_setup = openlifu.SimSetup(dt=2e-7, t_end=100e-6)\n", + "protocol = openlifu.Protocol(\n", " pulse=pulse,\n", " sequence=sequence,\n", " focal_pattern=focal_pattern,\n", @@ -96,7 +96,7 @@ "metadata": {}, "outputs": [], "source": [ - "pt = pyfus.Point(position=(0,0,30), units=\"mm\", radius=2)\n", + "pt = openlifu.Point(position=(0,0,30), units=\"mm\", radius=2)\n", "pts = protocol.focal_pattern.get_targets(pt)\n", "coords = protocol.sim_setup.get_coords()\n", "params = protocol.seg_method.ref_params(coords)\n", @@ -116,7 +116,7 @@ "metadata": {}, "outputs": [], "source": [ - "ds = pyfus.sim.run_simulation(arr=arr, \n", + "ds = openlifu.sim.run_simulation(arr=arr, \n", " params=params, \n", " delays=delays,\n", " apod= apod,\n", diff --git a/test_notebooks/test_gridweights.ipynb b/notebooks/test_gridweights.ipynb similarity index 99% rename from test_notebooks/test_gridweights.ipynb rename to notebooks/test_gridweights.ipynb index 516ce949..3fbe3ad3 100644 --- a/test_notebooks/test_gridweights.ipynb +++ b/notebooks/test_gridweights.ipynb @@ -8,7 +8,7 @@ "source": [ "import sys\n", "sys.path.append(R'C:\\Users\\pjh7\\git\\k-wave-python')\n", - "import pyfus\n", + "import openlifu\n", "from typing import List, Dict, Any, Tuple\n", "import logging\n", "import matplotlib.pyplot as plt\n", @@ -40,7 +40,7 @@ ], "source": [ "\n", - "db = pyfus.database.Database(path=R'C:/Users/pjh7/Documents/dom/')\n", + "db = openlifu.database.Database(path=R'C:/Users/pjh7/Documents/dom/')\n", "arr = db.load_transducer('vermon')\n", "trans_matrix = np.array(\n", " [[-1, 0, 0, 0],\n", @@ -49,7 +49,7 @@ " [0, 0, 0, 1]])\n", "arr.rescale(\"mm\")\n", "arr.matrix = trans_matrix\n", - "pt = pyfus.geo.Point(position=(5,-60,-8), units=\"mm\", radius=2)\n", + "pt = openlifu.geo.Point(position=(5,-60,-8), units=\"mm\", radius=2)\n", "plan = db.load_plan('example_plan')\n", "plan.sim_grid.dt = 2e-7\n", "plan.sim_grid.t_end = 100e-6\n", @@ -107,7 +107,7 @@ } ], "source": [ - "ds, output = pyfus.sim.run_simulation(arr=arr, \n", + "ds, output = openlifu.sim.run_simulation(arr=arr, \n", " params=params, \n", " delays=delays,\n", " apod= apod,\n", @@ -411,12 +411,12 @@ "outputs": [], "source": [ "\n", - "focal_pattern = pyfus.bf.RadialPattern(center=True, spoke_radius=5, num_spokes=5)\n", - "sim_grid = pyfus.bf.SimulationGrid(dt=2e-7, t_end=100e-6)\n", - "pulse = pyfus.bf.Pulse(frequency=400e3, duration=3/400e3)\n", - "sequence = pyfus.bf.Sequence()\n", - "bfp = pyfus.bf.BeamformingPlan()\n", - "plan = pyfus.plan.Plan(pulse=pulse,\n", + "focal_pattern = openlifu.bf.RadialPattern(center=True, spoke_radius=5, num_spokes=5)\n", + "sim_grid = openlifu.bf.SimulationGrid(dt=2e-7, t_end=100e-6)\n", + "pulse = openlifu.bf.Pulse(frequency=400e3, duration=3/400e3)\n", + "sequence = openlifu.bf.Sequence()\n", + "bfp = openlifu.bf.BeamformingPlan()\n", + "plan = openlifu.plan.Plan(pulse=pulse,\n", " sequence=sequence,\n", " focal_pattern=focal_pattern,\n", " sim_grid=sim_grid,\n", diff --git a/test_notebooks/test_nifti.ipynb b/notebooks/test_nifti.ipynb similarity index 98% rename from test_notebooks/test_nifti.ipynb rename to notebooks/test_nifti.ipynb index 589d8225..bbb69779 100644 --- a/test_notebooks/test_nifti.ipynb +++ b/notebooks/test_nifti.ipynb @@ -10,7 +10,7 @@ "slicer_exe = R\"C:\\Users\\pjh7\\AppData\\Local\\NA-MIC\\Slicer 5.2.2\\Slicer.exe\"\n", "import sys\n", "sys.path.append(modified_kwave_path)\n", - "import pyfus\n", + "import openlifu\n", "from typing import List, Dict, Any, Tuple\n", "import logging\n", "root = logging.getLogger()\n", @@ -39,7 +39,7 @@ ], "source": [ "\n", - "arr = pyfus.Transducer.gen_matrix_array(nx=8, ny=8, pitch=4, kerf=.5, units=\"mm\", impulse_response=1e5)\n", + "arr = openlifu.Transducer.gen_matrix_array(nx=8, ny=8, pitch=4, kerf=.5, units=\"mm\", impulse_response=1e5)\n", "trans_matrix = np.array(\n", " [[-1, 0, 0, 0],\n", " [0, .05, np.sqrt(1-.05**2), -105],\n", @@ -47,7 +47,7 @@ " [0, 0, 0, 1]])\n", "arr.rescale(\"mm\")\n", "arr.matrix = trans_matrix\n", - "pt = pyfus.Point(position=(5,-60,-8), units=\"mm\", radius=2)" + "pt = openlifu.Point(position=(5,-60,-8), units=\"mm\", radius=2)" ] }, { @@ -56,11 +56,11 @@ "metadata": {}, "outputs": [], "source": [ - "pulse = pyfus.Pulse(frequency=400e3, duration=3/400e3)\n", - "sequence = pyfus.Sequence()\n", - "focal_pattern = pyfus.focal_patterns.Wheel(center=True, spoke_radius=5, num_spokes=5)\n", - "sim_setup = pyfus.SimSetup(dt=2e-7, t_end=100e-6)\n", - "protocol = pyfus.Protocol(\n", + "pulse = openlifu.Pulse(frequency=400e3, duration=3/400e3)\n", + "sequence = openlifu.Sequence()\n", + "focal_pattern = openlifu.focal_patterns.Wheel(center=True, spoke_radius=5, num_spokes=5)\n", + "sim_setup = openlifu.SimSetup(dt=2e-7, t_end=100e-6)\n", + "protocol = openlifu.Protocol(\n", " pulse=pulse,\n", " sequence=sequence,\n", " focal_pattern=focal_pattern,\n", @@ -112,7 +112,7 @@ } ], "source": [ - "ds = pyfus.sim.run_simulation(arr=arr, \n", + "ds = openlifu.sim.run_simulation(arr=arr, \n", " params=params, \n", " delays=delays,\n", " apod= apod,\n", diff --git a/test_notebooks/test_registers.ipynb b/notebooks/test_registers.ipynb similarity index 99% rename from test_notebooks/test_registers.ipynb rename to notebooks/test_registers.ipynb index 5e39c543..ea983a46 100644 --- a/test_notebooks/test_registers.ipynb +++ b/notebooks/test_registers.ipynb @@ -6,7 +6,7 @@ "metadata": {}, "outputs": [], "source": [ - "from pyfus.io.ustx import PulseProfile, DelayProfile, TxModule, Tx7332Registers, print_regs, TxArray, swap_byte_order\n", + "from openlifu.io.ustx import PulseProfile, DelayProfile, TxModule, Tx7332Registers, print_regs, TxArray, swap_byte_order\n", "import numpy as np" ] }, diff --git a/pyfus/seg/seg_methods/tissue.py b/pyfus/seg/seg_methods/tissue.py deleted file mode 100644 index 13f16aab..00000000 --- a/pyfus/seg/seg_methods/tissue.py +++ /dev/null @@ -1,6 +0,0 @@ -from dataclasses import dataclass -from pyfus.seg.seg_methods.seg_method import UniformSegmentation - -@dataclass -class Tissue(UniformSegmentation): - ref_material: str = "tissue" \ No newline at end of file diff --git a/pyfus/seg/seg_methods/water.py b/pyfus/seg/seg_methods/water.py deleted file mode 100644 index 3c935c06..00000000 --- a/pyfus/seg/seg_methods/water.py +++ /dev/null @@ -1,6 +0,0 @@ -from dataclasses import dataclass -from pyfus.seg.seg_methods.seg_method import UniformSegmentation - -@dataclass -class Water(UniformSegmentation): - ref_material: str = "water" \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index db516893..e4821e92 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [project] -name = "open_pyfus" +name = "openlifu" version = "0.1.0" description = "Openwater Focused Ultrasound Toolkit" dependencies = [ @@ -19,7 +19,3 @@ dependencies = [ [tool.setuptools] include-package-data = false - -[tool.setuptools.packages.find] -include = ["pyfus*"] -exclude = ["test*", "env*"] \ No newline at end of file diff --git a/pyfus/__init__.py b/src/openlifu/__init__.py similarity index 62% rename from pyfus/__init__.py rename to src/openlifu/__init__.py index 725a4437..aa745df0 100644 --- a/pyfus/__init__.py +++ b/src/openlifu/__init__.py @@ -1,27 +1,27 @@ """ -pyFUS: Python Focused Ultrasound +openlifu: Python Focused Ultrasound ================================ -This package contains the modules for pyFUS. +This package contains the modules for openlifu. """ #from . import bf, db, io, plan, seg, sim, xdc, geo -from pyfus.geo import ( +from openlifu.geo import ( Point ) -from pyfus.xdc import ( +from openlifu.xdc import ( Transducer ) -from pyfus.plan import ( +from openlifu.plan import ( Protocol, Solution ) -from pyfus.seg import ( +from openlifu.seg import ( Material, SegmentationMethod, seg_methods, @@ -33,7 +33,7 @@ STANDOFF ) -from pyfus.bf import ( +from openlifu.bf import ( DelayMethod, ApodizationMethod, Pulse, @@ -44,10 +44,10 @@ apod_methods ) -from pyfus.sim import ( +from openlifu.sim import ( SimSetup ) -from pyfus.db import ( +from openlifu.db import ( Database -) \ No newline at end of file +) diff --git a/pyfus/bf/__init__.py b/src/openlifu/bf/__init__.py similarity index 100% rename from pyfus/bf/__init__.py rename to src/openlifu/bf/__init__.py diff --git a/pyfus/bf/apod_methods/__init__.py b/src/openlifu/bf/apod_methods/__init__.py similarity index 100% rename from pyfus/bf/apod_methods/__init__.py rename to src/openlifu/bf/apod_methods/__init__.py diff --git a/pyfus/bf/apod_methods/apodmethod.py b/src/openlifu/bf/apod_methods/apodmethod.py similarity index 80% rename from pyfus/bf/apod_methods/apodmethod.py rename to src/openlifu/bf/apod_methods/apodmethod.py index d46d689b..43716e74 100644 --- a/pyfus/bf/apod_methods/apodmethod.py +++ b/src/openlifu/bf/apod_methods/apodmethod.py @@ -1,9 +1,9 @@ from dataclasses import dataclass, field from abc import ABC, abstractmethod import xarray as xa -from pyfus.xdc import Transducer -from pyfus.geo import Point -from pyfus.bf import apod_methods +from openlifu.xdc import Transducer +from openlifu.geo import Point +from openlifu.bf import apod_methods @dataclass class ApodizationMethod(ABC): @@ -15,11 +15,11 @@ def to_dict(self): d = self.__dict__.copy() d['class'] = self.__class__.__name__ return d - + @staticmethod def from_dict(d): d = d.copy() short_classname = d.pop("class") module_dict = apod_methods.__dict__ class_constructor = module_dict[short_classname] - return class_constructor(**d) \ No newline at end of file + return class_constructor(**d) diff --git a/pyfus/bf/apod_methods/maxangle.py b/src/openlifu/bf/apod_methods/maxangle.py similarity index 81% rename from pyfus/bf/apod_methods/maxangle.py rename to src/openlifu/bf/apod_methods/maxangle.py index 25270cfd..f5362b92 100644 --- a/pyfus/bf/apod_methods/maxangle.py +++ b/src/openlifu/bf/apod_methods/maxangle.py @@ -1,9 +1,9 @@ from dataclasses import dataclass import xarray as xa import numpy as np -from pyfus.xdc import Transducer -from pyfus.geo import Point -from pyfus.bf.apod_methods import ApodizationMethod +from openlifu.xdc import Transducer +from openlifu.geo import Point +from openlifu.bf.apod_methods import ApodizationMethod @dataclass class MaxAngle(ApodizationMethod): @@ -15,4 +15,4 @@ def calc_apodization(self, arr: Transducer, target: Point, params: xa.Dataset, t angles = np.array([el.angle_to_point(target_pos, units="m", matrix=matrix, return_as=self.units) for el in arr.elements]) apod = np.zeros(arr.numelements()) apod[angles <= self.max_angle] = 1 - return apod \ No newline at end of file + return apod diff --git a/pyfus/bf/apod_methods/piecewiselinear.py b/src/openlifu/bf/apod_methods/piecewiselinear.py similarity index 83% rename from pyfus/bf/apod_methods/piecewiselinear.py rename to src/openlifu/bf/apod_methods/piecewiselinear.py index 5918fbd6..f5985ed9 100644 --- a/pyfus/bf/apod_methods/piecewiselinear.py +++ b/src/openlifu/bf/apod_methods/piecewiselinear.py @@ -1,9 +1,9 @@ from dataclasses import dataclass import xarray as xa import numpy as np -from pyfus.xdc import Transducer -from pyfus.geo import Point -from pyfus.bf.apod_methods import ApodizationMethod +from openlifu.xdc import Transducer +from openlifu.geo import Point +from openlifu.bf.apod_methods import ApodizationMethod @dataclass class PiecewiseLinear(ApodizationMethod): @@ -17,4 +17,4 @@ def calc_apodization(self, arr: Transducer, target: Point, params: xa.Dataset, t apod = np.zeros(arr.numelements()) f = ((self.zero_angle - angles) / (self.zero_angle - self.rolloff_angle)) apod = np.maximum(0, np.minimum(1, f)) - return apod \ No newline at end of file + return apod diff --git a/pyfus/bf/apod_methods/uniform.py b/src/openlifu/bf/apod_methods/uniform.py similarity index 57% rename from pyfus/bf/apod_methods/uniform.py rename to src/openlifu/bf/apod_methods/uniform.py index c2756581..bb3b343b 100644 --- a/pyfus/bf/apod_methods/uniform.py +++ b/src/openlifu/bf/apod_methods/uniform.py @@ -1,12 +1,12 @@ from dataclasses import dataclass import xarray as xa import numpy as np -from pyfus.xdc import Transducer -from pyfus.geo import Point -from pyfus.bf.apod_methods import ApodizationMethod +from openlifu.xdc import Transducer +from openlifu.geo import Point +from openlifu.bf.apod_methods import ApodizationMethod @dataclass class Uniform(ApodizationMethod): value = 1 def calc_apodization(self, arr: Transducer, target: Point, params: xa.Dataset, transform: bool = True): - return np.full(arr.numelements(), self.value) \ No newline at end of file + return np.full(arr.numelements(), self.value) diff --git a/pyfus/bf/delay_methods/__init__.py b/src/openlifu/bf/delay_methods/__init__.py similarity index 57% rename from pyfus/bf/delay_methods/__init__.py rename to src/openlifu/bf/delay_methods/__init__.py index 69d2fe87..04477f3d 100644 --- a/pyfus/bf/delay_methods/__init__.py +++ b/src/openlifu/bf/delay_methods/__init__.py @@ -1,2 +1,2 @@ from .delaymethod import DelayMethod -from .direct import Direct \ No newline at end of file +from .direct import Direct diff --git a/pyfus/bf/delay_methods/delaymethod.py b/src/openlifu/bf/delay_methods/delaymethod.py similarity index 80% rename from pyfus/bf/delay_methods/delaymethod.py rename to src/openlifu/bf/delay_methods/delaymethod.py index 1ac94bec..486954e9 100644 --- a/pyfus/bf/delay_methods/delaymethod.py +++ b/src/openlifu/bf/delay_methods/delaymethod.py @@ -1,9 +1,9 @@ from dataclasses import dataclass, field from abc import ABC, abstractmethod import xarray as xa -from pyfus.xdc import Transducer -from pyfus.geo import Point -from pyfus.bf import delay_methods +from openlifu.xdc import Transducer +from openlifu.geo import Point +from openlifu.bf import delay_methods @dataclass class DelayMethod(ABC): @@ -15,11 +15,11 @@ def to_dict(self): d = self.__dict__.copy() d['class'] = self.__class__.__name__ return d - + @staticmethod def from_dict(d): d = d.copy() short_classname = d.pop("class") module_dict = delay_methods.__dict__ class_constructor = module_dict[short_classname] - return class_constructor(**d) \ No newline at end of file + return class_constructor(**d) diff --git a/pyfus/bf/delay_methods/direct.py b/src/openlifu/bf/delay_methods/direct.py similarity index 86% rename from pyfus/bf/delay_methods/direct.py rename to src/openlifu/bf/delay_methods/direct.py index c1bc4204..7a3619d8 100644 --- a/pyfus/bf/delay_methods/direct.py +++ b/src/openlifu/bf/delay_methods/direct.py @@ -1,9 +1,9 @@ from dataclasses import dataclass import xarray as xa import numpy as np -from pyfus.xdc import Transducer -from pyfus.geo import Point -from pyfus.bf.delay_methods import DelayMethod +from openlifu.xdc import Transducer +from openlifu.geo import Point +from openlifu.bf.delay_methods import DelayMethod from typing import ClassVar, Optional @dataclass diff --git a/pyfus/bf/focal_patterns/__init__.py b/src/openlifu/bf/focal_patterns/__init__.py similarity index 74% rename from pyfus/bf/focal_patterns/__init__.py rename to src/openlifu/bf/focal_patterns/__init__.py index e9ad037a..edb0d9a6 100644 --- a/pyfus/bf/focal_patterns/__init__.py +++ b/src/openlifu/bf/focal_patterns/__init__.py @@ -1,3 +1,3 @@ from .focal_pattern import FocalPattern from .single import SinglePoint -from .wheel import Wheel \ No newline at end of file +from .wheel import Wheel diff --git a/pyfus/bf/focal_patterns/focal_pattern.py b/src/openlifu/bf/focal_patterns/focal_pattern.py similarity index 89% rename from pyfus/bf/focal_patterns/focal_pattern.py rename to src/openlifu/bf/focal_patterns/focal_pattern.py index d959149b..d712e71f 100644 --- a/pyfus/bf/focal_patterns/focal_pattern.py +++ b/src/openlifu/bf/focal_patterns/focal_pattern.py @@ -1,7 +1,7 @@ from abc import ABC, abstractmethod from dataclasses import dataclass -from pyfus.geo import Point -from pyfus.bf import focal_patterns +from openlifu.geo import Point +from openlifu.bf import focal_patterns @dataclass class FocalPattern(ABC): @@ -16,7 +16,7 @@ class FocalPattern(ABC): def get_targets(self, target: Point): """ Get the targets of the focal pattern - + :param target: Target point of the focal pattern :returns: List of target points """ @@ -45,12 +45,12 @@ def to_dict(self): def from_dict(d): """ Create a focal pattern from a dictionary - + :param d: Dictionary of the focal pattern parameters :returns: FocalPattern object """ - d = d.copy() + d = d.copy() short_classname = d.pop("class") module_dict = focal_patterns.__dict__ class_constructor = module_dict[short_classname] - return class_constructor(**d) \ No newline at end of file + return class_constructor(**d) diff --git a/pyfus/bf/focal_patterns/single.py b/src/openlifu/bf/focal_patterns/single.py similarity index 85% rename from pyfus/bf/focal_patterns/single.py rename to src/openlifu/bf/focal_patterns/single.py index 48c4036b..e7c65eb6 100644 --- a/pyfus/bf/focal_patterns/single.py +++ b/src/openlifu/bf/focal_patterns/single.py @@ -1,6 +1,6 @@ from dataclasses import dataclass -from pyfus.bf.focal_patterns import FocalPattern -from pyfus.geo import Point +from openlifu.bf.focal_patterns import FocalPattern +from openlifu.geo import Point @dataclass class SinglePoint(FocalPattern): @@ -12,7 +12,7 @@ class SinglePoint(FocalPattern): def get_targets(self, target: Point): """ Get the targets of the focal pattern - + :param target: Target point of the focal pattern :returns: List of target points """ @@ -24,4 +24,4 @@ def num_foci(self): :returns: Number of foci (1) """ - return 1 \ No newline at end of file + return 1 diff --git a/pyfus/bf/focal_patterns/wheel.py b/src/openlifu/bf/focal_patterns/wheel.py similarity index 92% rename from pyfus/bf/focal_patterns/wheel.py rename to src/openlifu/bf/focal_patterns/wheel.py index afb7cd82..d993e26f 100644 --- a/pyfus/bf/focal_patterns/wheel.py +++ b/src/openlifu/bf/focal_patterns/wheel.py @@ -1,6 +1,6 @@ from dataclasses import dataclass -from pyfus.bf.focal_patterns import FocalPattern -from pyfus.geo import Point +from openlifu.bf.focal_patterns import FocalPattern +from openlifu.geo import Point import numpy as np @dataclass @@ -16,7 +16,7 @@ class Wheel(FocalPattern): """ center: bool = True num_spokes: int = 4 - spoke_radius: float = 1.0 # mm + spoke_radius: float = 1.0 # mm units: str = "mm" def get_targets(self, target: Point): @@ -30,7 +30,7 @@ def get_targets(self, target: Point): targets = [target.copy()] targets[0].id = f"{target.id}_center" targets[0].id = f"{target.id} (Center)" - else: + else: targets = [] m = target.get_matrix(center_on_point=True) for i in range(self.num_spokes): @@ -44,11 +44,11 @@ def get_targets(self, target: Point): radius=target.radius) targets.append(spoke) return targets - + def num_foci(self): """ Get the number of foci in the focal pattern - + :returns: Number of foci """ return int(self.center) + self.num_spokes diff --git a/pyfus/bf/pulse.py b/src/openlifu/bf/pulse.py similarity index 97% rename from pyfus/bf/pulse.py rename to src/openlifu/bf/pulse.py index 04382a02..31bdd667 100644 --- a/pyfus/bf/pulse.py +++ b/src/openlifu/bf/pulse.py @@ -6,7 +6,7 @@ class Pulse: """ Class for representing a sinusoidal pulse - + :ivar frequency: Frequency of the pulse in Hz :ivar amplitude: Amplitude of the pulse in Pa :ivar duration: Duration of the pulse in s @@ -19,12 +19,12 @@ class Pulse: def calc_pulse(self, t: np.array): """ Calculate the pulse at the given times - + :param t: Array of times to calculate the pulse at (s) :returns: Array of pulse values at the given times """ return self.amplitude * np.sin(2*np.pi*self.frequency*t) - + def calc_time(self, dt: float): """ Calculate the time array for the pulse for a particular timestep @@ -33,18 +33,18 @@ def calc_time(self, dt: float): :returns: Array of times for the pulse (s) """ return np.arange(0, self.duration, dt) - + def get_table(self): """ Get a table of the pulse parameters - + :returns: Pandas DataFrame of the pulse parameters """ records = [{"Name": "Frequency", "Value": self.frequency, "Unit": "Hz"}, {"Name": "Amplitude", "Value": self.amplitude, "Unit": "Pa"}, {"Name": "Duration", "Value": self.duration, "Unit": "s"}] return pd.DataFrame.from_records(records) - + def to_dict(self): """ Convert the pulse to a dictionary @@ -55,7 +55,7 @@ def to_dict(self): "amplitude": self.amplitude, "duration": self.duration, "class": "Pulse"} - + @staticmethod def from_dict(d): """ @@ -65,4 +65,3 @@ def from_dict(d): :returns: Pulse object """ return Pulse(frequency=d["frequency"], amplitude=d["amplitude"], duration=d["duration"]) - \ No newline at end of file diff --git a/pyfus/bf/sequence.py b/src/openlifu/bf/sequence.py similarity index 89% rename from pyfus/bf/sequence.py rename to src/openlifu/bf/sequence.py index 8bd1f254..ab41fa47 100644 --- a/pyfus/bf/sequence.py +++ b/src/openlifu/bf/sequence.py @@ -27,7 +27,7 @@ def get_table(self): {"Name": "Pulse Train Interval", "Value": self.pulse_train_interval, "Unit": "s"}, {"Name": "Pulse Train Count", "Value": self.pulse_train_count, "Unit": ""}] return pd.DataFrame.from_records(records) - + @staticmethod def from_dict(d): """ @@ -36,18 +36,18 @@ def from_dict(d): :param d: Dictionary of the sequence parameters :returns: Sequence object """ - return Sequence(pulse_interval=d["pulse_interval"], + return Sequence(pulse_interval=d["pulse_interval"], pulse_count=d["pulse_count"], - pulse_train_interval=d["pulse_train_interval"], + pulse_train_interval=d["pulse_train_interval"], pulse_train_count=d["pulse_train_count"]) - + def to_dict(self): """ Convert the sequence to a dictionary :returns: Dictionary of the sequence parameters """ - return {"pulse_interval": self.pulse_interval, + return {"pulse_interval": self.pulse_interval, "pulse_count": self.pulse_count, - "pulse_train_interval": self.pulse_train_interval, - "pulse_train_count": self.pulse_train_count} \ No newline at end of file + "pulse_train_interval": self.pulse_train_interval, + "pulse_train_count": self.pulse_train_count} diff --git a/pyfus/db/__init__.py b/src/openlifu/db/__init__.py similarity index 100% rename from pyfus/db/__init__.py rename to src/openlifu/db/__init__.py diff --git a/pyfus/db/database.py b/src/openlifu/db/database.py similarity index 99% rename from pyfus/db/database.py rename to src/openlifu/db/database.py index 788e2363..cee86aa4 100644 --- a/pyfus/db/database.py +++ b/src/openlifu/db/database.py @@ -8,8 +8,8 @@ import h5py import glob from typing import Literal, Optional -from pyfus.plan import Protocol, Solution -from pyfus.db import Subject +from openlifu.plan import Protocol, Solution +from openlifu.db import Subject OnConflictOpts = Literal['error', 'overwrite', 'skip'] @@ -40,7 +40,7 @@ def add_protocol(self, protocol: Protocol, on_conflict: OnConflictOpts = "error" # Check if the sonication protocol ID already exists in the database protocol_id = protocol.id protocol_ids = self.get_protocol_ids() - + if protocol_id in protocol_ids: if on_conflict == "error": raise ValueError(f"Protocol with ID {protocol_id} already exists in the database.") @@ -342,12 +342,12 @@ def get_transducer_ids(self): self.logger.warning("Transducers file not found.") return [] - def load_gridweights(self, transducer_id, grid_hash): + def load_gridweights(self, transducer_id, grid_hash): gridweight_filename = self.get_gridweights_filename(transducer_id, grid_hash) with h5py.File(gridweight_filename, "r") as f: grid_weights = f["grid_weights"][:] return grid_weights - + def load_subject(self, subject_id, options=None): subject_filename = self.get_subject_filename(subject_id) subject = Subject.from_file(subject_filename) @@ -380,7 +380,7 @@ def load_system(self, sys_id=None): return sys def load_transducer(self, transducer_id): - from pyfus.xdc import Transducer + from openlifu.xdc import Transducer transducer_filename = self.get_transducer_filename(transducer_id) transducer = Transducer.from_file(transducer_filename) return transducer @@ -564,7 +564,7 @@ def write_transducer_ids(self, transducer_ids): def get_default_user_dir(): """ Get the default user directory for the database - + :returns: Default user directory """ return os.path.expanduser("~") @@ -573,7 +573,7 @@ def get_default_user_dir(): def get_default_path(options=None): """ Get the default path for the database - + :returns: Default path for the database """ return os.path.join(Database.get_default_user_dir(), "Documents", "db") diff --git a/pyfus/db/session.py b/src/openlifu/db/session.py similarity index 94% rename from pyfus/db/session.py rename to src/openlifu/db/session.py index 1b7d164d..9877b236 100644 --- a/pyfus/db/session.py +++ b/src/openlifu/db/session.py @@ -1,9 +1,9 @@ from dataclasses import dataclass, field from typing import Optional, List from datetime import datetime -from pyfus.geo import Point -from pyfus.xdc import Transducer -from pyfus.util.strings import sanitize +from openlifu.geo import Point +from openlifu.xdc import Transducer +from openlifu.util.strings import sanitize import xarray @dataclass @@ -53,7 +53,7 @@ def __post_init__(self): def from_dict(d): """ Create a session from a dictionary - + :param d: Dictionary of session parameters :returns: Session object """ @@ -82,11 +82,11 @@ def from_dict(d): elif isinstance(d['markers'], Point): d['markers'] = [d['markers']] return Session(**d) - + def to_dict(self): """ Convert the session to a dictionary - + :returns: Dictionary of session parameters """ d = self.__dict__.copy() @@ -97,12 +97,12 @@ def to_dict(self): d['targets'] = [p.to_dict() for p in d['targets']] d['markers'] = [p.to_dict() for p in d['markers']] return d - + def to_file(self, filename): """ Save the session to a file - + :param filename: Name of the file """ - from pyfus.util.json import to_json - to_json(self.to_dict(), filename) \ No newline at end of file + from openlifu.util.json import to_json + to_json(self.to_dict(), filename) diff --git a/pyfus/db/subject.py b/src/openlifu/db/subject.py similarity index 90% rename from pyfus/db/subject.py rename to src/openlifu/db/subject.py index 36284087..1fe64b0e 100644 --- a/pyfus/db/subject.py +++ b/src/openlifu/db/subject.py @@ -1,6 +1,6 @@ from dataclasses import dataclass, field from typing import List, Optional -from pyfus.util.strings import sanitize +from openlifu.util.strings import sanitize import json @dataclass @@ -34,12 +34,12 @@ def __post_init__(self): def from_dict(d): """ Create a subject from a dictionary - + :param d: Dictionary of subject parameters :returns: Subject object """ return Subject(**d) - + @staticmethod def from_file(filename): """ @@ -50,21 +50,21 @@ def from_file(filename): """ with open(filename, 'r') as f: return Subject.from_dict(json.load(f)) - + def to_dict(self): """ Convert the subject to a dictionary - + :returns: Dictionary of subject parameters """ return self.__dict__.copy() - + def to_file(self, filename): """ Write the subject to a file - + :param filename: Name of the file to write """ - from pyfus.util.json import to_json + from openlifu.util.json import to_json with open(filename, 'w') as f: - json.dump(self.to_dict(), f, indent=4) \ No newline at end of file + json.dump(self.to_dict(), f, indent=4) diff --git a/pyfus/geo.py b/src/openlifu/geo.py similarity index 95% rename from pyfus/geo.py rename to src/openlifu/geo.py index ee8146b8..df52e2b5 100644 --- a/pyfus/geo.py +++ b/src/openlifu/geo.py @@ -1,7 +1,7 @@ from typing import Any, Tuple, Optional import numpy as np from dataclasses import dataclass, field -from pyfus.util.units import getunitconversion +from openlifu.util.units import getunitconversion import copy import vtk @@ -19,7 +19,7 @@ def __post_init__(self): if len(self.position) != len(self.dims): raise ValueError("Position and dims must have same length.") self.position = np.array(self.position).reshape(3) - + def copy(self): return copy.deepcopy(self) @@ -48,7 +48,7 @@ def get_matrix(self, origin: np.ndarray = np.eye(4), center_on_point: bool = Fal if not local: m = np.dot(origin, m) return m - + def get_polydata(self, transform: np.ndarray = np.eye(4), units=None): units = self.units if units is None else units colors = vtk.vtkNamedColors() @@ -77,9 +77,9 @@ def rescale(self, units: str): self.radius = self.radius * scl self.units = units - def transform(self, - matrix: np.ndarray, - units: Optional[str] = None, + def transform(self, + matrix: np.ndarray, + units: Optional[str] = None, new_dims: Optional[Tuple[str, str, str]]=None): if units is not None: self.rescale(units) diff --git a/pyfus/io/__init__.py b/src/openlifu/io/__init__.py similarity index 58% rename from pyfus/io/__init__.py rename to src/openlifu/io/__init__.py index ba0a6c28..536178af 100644 --- a/pyfus/io/__init__.py +++ b/src/openlifu/io/__init__.py @@ -1 +1 @@ -from .ustx import PulseProfile, DelayProfile, Tx7332Registers, TxModule, TxArray, print_regs, swap_byte_order \ No newline at end of file +from .ustx import PulseProfile, DelayProfile, Tx7332Registers, TxModule, TxArray, print_regs, swap_byte_order diff --git a/pyfus/io/ustx.py b/src/openlifu/io/ustx.py similarity index 97% rename from pyfus/io/ustx.py rename to src/openlifu/io/ustx.py index 60c85c96..42d32232 100644 --- a/pyfus/io/ustx.py +++ b/src/openlifu/io/ustx.py @@ -1,6 +1,6 @@ from dataclasses import dataclass, field from typing import Tuple, Optional, List, Dict, Literal -from pyfus.util.units import getunitconversion +from openlifu.util.units import getunitconversion import numpy as np import logging @@ -28,8 +28,8 @@ ADDRESS_DYNPWR_1, ADDRESS_LDO_PWR_2, ADDRESS_TRSW_TURNON, - ADDRESS_DELAY_SEL, - ADDRESS_PATTERN_MODE, + ADDRESS_DELAY_SEL, + ADDRESS_PATTERN_MODE, ADDRESS_PATTERN_REPEAT, ADDRESS_PATTERN_SEL_G1, ADDRESS_PATTERN_SEL_G2, @@ -91,7 +91,7 @@ def get_delay_location(channel:int, profile:int=1): """ Gets the address and least significant bit of a delay - + :param channel: Channel number :param profile: Delay profile number :returns: Register address and least significant bit of the delay location @@ -149,7 +149,7 @@ def calc_pulse_pattern(frequency:float, duty_cycle:float=DEFAULT_PATTERN_DUTY_CY :returns: Tuple of lists of levels and lengths, and the clock divider setting """ clk_div_n = 0 - while clk_div_n < 6: + while clk_div_n < 6: clk_n = bf_clk / (2**clk_div_n) period_samples = int(clk_n / frequency) first_half_period_samples = int(period_samples / 2) @@ -183,7 +183,7 @@ def calc_pulse_pattern(frequency:float, duty_cycle:float=DEFAULT_PATTERN_DUTY_CY else: per_lengths.append(MAX_PATTERN_PERIOD_LENGTH) samples -= (MAX_PATTERN_PERIOD_LENGTH+2) - per_levels.append(levels[i]) + per_levels.append(levels[i]) else: per_lengths.append(samples-2) per_levels.append(levels[i]) @@ -191,8 +191,8 @@ def calc_pulse_pattern(frequency:float, duty_cycle:float=DEFAULT_PATTERN_DUTY_CY if len(per_levels) <= MAX_PATTERN_PERIODS: t = (np.arange(np.sum(np.array(per_lengths)+2))*(1/clk_n)).tolist() y = np.concatenate([[yi]*(ni+2) for yi,ni in zip(per_levels, per_lengths)]).tolist() - pattern = {'levels': per_levels, - 'lengths': per_lengths, + pattern = {'levels': per_levels, + 'lengths': per_lengths, 'clk_div_n': clk_div_n, 't': t, 'y': y} @@ -229,9 +229,9 @@ def print_regs(d): def pack_registers(regs, pack_single:bool=False): """ Packs registers into contiguous blocks - + :param regs: Dictionary of registers - :param pack_single: Pack single registers into arrays. Default True. + :param pack_single: Pack single registers into arrays. Default True. :returns: Dictionary of packed registers. """ addresses = sorted(regs.keys()) @@ -256,7 +256,7 @@ def pack_registers(regs, pack_single:bool=False): def swap_byte_order(regs): """ Swaps the byte order of the registers - + :param regs: Dictionary of registers :returns: Dictionary of registers with swapped byte order """ @@ -283,7 +283,7 @@ def __post_init__(self): raise ValueError(f"Apodizations list must have {self.num_elements} elements") if self.profile not in VALID_DELAY_PROFILES: raise ValueError(f"Invalid Profile {self.profile}") - + @dataclass class PulseProfile: profile: int @@ -292,11 +292,11 @@ class PulseProfile: duty_cycle: float=DEFAULT_PATTERN_DUTY_CYCLE tail_count: int=DEFAULT_TAIL_COUNT invert: bool=False - + def __post_init__(self): if self.profile not in VALID_PATTERN_PROFILES: raise ValueError(f"Invalid profile {self.profile}.") - + @dataclass class Tx7332Registers: bf_clk: float = DEFAULT_CLK_FREQ @@ -383,10 +383,10 @@ def get_pulse_profile(self, profile: Optional[int]=None) -> PulseProfile: raise ValueError(f"Pulse profile {profile} not found") profile = profiles.index(profile) return self._pulse_profiles_list[profile] - + def configured_pulse_profiles(self) -> List[int]: return [p.profile for p in self._pulse_profiles_list] - + def activate_delay_profile(self, profile:int): if profile not in self.configured_delay_profiles(): raise ValueError(f"Delay profile {profile} not configured") @@ -407,9 +407,9 @@ def get_delay_control_registers(self, profile: Optional[int]=None) -> Dict[int,i delay_sel_register = 0 delay_sel_register = set_register_value(delay_sel_register, delay_profile.profile-1, lsb=12, width=4) delay_sel_register = set_register_value(delay_sel_register, delay_profile.profile-1, lsb=28, width=4) - return {ADDRESS_DELAY_SEL: delay_sel_register, + return {ADDRESS_DELAY_SEL: delay_sel_register, ADDRESS_APODIZATION: apod_register} - + def get_pulse_control_registers(self, profile: Optional[int]=None) -> Dict[int,int]: if profile is None: profile = self.active_pulse_profile @@ -437,7 +437,7 @@ def get_pulse_control_registers(self, profile: Optional[int]=None) -> Dict[int,i raise ValueError(f"Pattern duration too long for elastic repeat") else: repeat = cycles-1 - elastic_repeat = 0 + elastic_repeat = 0 elastic_mode = 0 y = pattern['y']*(repeat+1) y = np.array(y + [0]*pulse_profile.tail_count) @@ -450,7 +450,7 @@ def get_pulse_control_registers(self, profile: Optional[int]=None) -> Dict[int,i reg_repeat = set_register_value(reg_repeat, elastic_mode, lsb=11, width=1) reg_repeat = set_register_value(reg_repeat, elastic_repeat, lsb=12, width=16) reg_pat_sel = 0 - reg_pat_sel = set_register_value(reg_pat_sel, pulse_profile.profile-1, lsb=0, width=6) + reg_pat_sel = set_register_value(reg_pat_sel, pulse_profile.profile-1, lsb=0, width=6) registers = {ADDRESS_PATTERN_MODE: reg_mode, ADDRESS_PATTERN_REPEAT: reg_repeat, ADDRESS_PATTERN_SEL_G1: reg_pat_sel, @@ -470,8 +470,8 @@ def get_delay_data_registers(self, profile: Optional[int]=None, pack: bool=False data_registers[address] = set_register_value(data_registers[address], delay_value, lsb=lsb, width=DELAY_WIDTH) if pack: data_registers = pack_registers(data_registers, pack_single=pack_single) - return data_registers - + return data_registers + def get_pulse_data_registers(self, profile: Optional[int]=None, pack: bool=False, pack_single: bool=False) -> Dict[int,int]: if profile is None: profile = self.active_pulse_profile @@ -507,12 +507,12 @@ def get_registers(self, profiles: ProfileOpts = "configured", pack: bool=False, raise ValueError(f"No delay profile activated") if self.active_pulse_profile is None: raise ValueError(f"No pulse profile activated") - registers = {addr:0x0 for addr in ADDRESSES_GLOBAL} + registers = {addr:0x0 for addr in ADDRESSES_GLOBAL} registers.update(self.get_delay_control_registers()) registers.update(self.get_pulse_control_registers()) if profiles == "active": delay_data = self.get_delay_data_registers(pack=pack, pack_single=pack_single) - pulse_data = self.get_pulse_data_registers(pack=pack, pack_single=pack_single) + pulse_data = self.get_pulse_data_registers(pack=pack, pack_single=pack_single) else: if profiles == "all": delay_data = {addr:0x0 for addr in ADDRESSES_DELAY_DATA} @@ -543,7 +543,7 @@ class TxModule: def __post_init__(self): self.transmitters = tuple([Tx7332Registers(bf_clk=self.bf_clk) for _ in range(self.num_transmitters)]) - + def add_pulse_profile(self, pulse_profile: PulseProfile, activate: Optional[bool]=None): """ Add a pulse profile @@ -562,12 +562,12 @@ def add_pulse_profile(self, pulse_profile: PulseProfile, activate: Optional[bool if activate: self.active_pulse_profile = pulse_profile.profile for tx in self.transmitters: - tx.add_pulse_profile(pulse_profile, activate = activate) - + tx.add_pulse_profile(pulse_profile, activate = activate) + def add_delay_profile(self, delay_profile: DelayProfile, activate: Optional[bool]=None): """ Add a delay profile - + :param p: Delay profile :param activate: Activate the delay profile """ @@ -610,7 +610,7 @@ def remove_delay_profile(self, profile:int): def remove_pulse_profile(self, profile:int): """ Remove a pulse profile - + :param profile: Pulse profile number """ profiles = self.configured_pulse_profiles() @@ -636,12 +636,12 @@ def get_delay_profile(self, profile:Optional[int]=None) -> DelayProfile: if profile not in profiles: raise ValueError(f"Delay profile {profile} not found") i = profiles.index(profile) - return self._delay_profiles_list[i] - + return self._delay_profiles_list[i] + def _configured_delay_profiles(self) -> List[int]: """ Get the configured delay profiles - + :return: List of delay profiles """ return [p.profile for p in self._delay_profiles_list] @@ -660,11 +660,11 @@ def get_pulse_profile(self, profile:Optional[int]=None) -> PulseProfile: raise ValueError(f"Pulse profile {profile} not found") i = profiles.index(profile) return self._pulse_profiles_list[i] - + def configured_pulse_profiles(self) -> List[int]: """ Get the configured pulse profiles - + :return: List of pulse profiles """ return [p.profile for p in self._pulse_profiles_list] @@ -672,17 +672,17 @@ def configured_pulse_profiles(self) -> List[int]: def activate_delay_profile(self, profile:int=1): """ Activates a delay profile - + :param profile: Delay profile number """ for tx in self.transmitters: - tx.activate_delay_profile(profile) + tx.activate_delay_profile(profile) self.active_delay_profile = profile def activate_pulse_profile(self, profile:int=1): """ Activates a pulse profile - + :param profile: Pulse profile number """ for tx in self.transmitters: @@ -723,7 +723,7 @@ def get_registers(self, profiles: ProfileOpts = "configured", recompute: bool = self.recompute_delay_profiles() self.recompute_pulse_profiles() return [tx.get_registers(profiles, pack=pack, pack_single=pack_single) for tx in self.transmitters] - + def get_delay_control_registers(self, profile:Optional[int]=None) -> List[Dict[int,int]]: """ Get the delay control registers for all transmitters @@ -734,7 +734,7 @@ def get_delay_control_registers(self, profile:Optional[int]=None) -> List[Dict[i if profile is None: profile = self.active_delay_profile return [tx.get_delay_control_registers(profile) for tx in self.transmitters] - + def get_pulse_control_registers(self, profile:Optional[int]=None) -> List[Dict[int,int]]: """ Get the pulse control registers for all transmitters @@ -745,7 +745,7 @@ def get_pulse_control_registers(self, profile:Optional[int]=None) -> List[Dict[i if profile is None: profile = self.active_pulse_profile return [tx.get_pulse_control_registers(profile) for tx in self.transmitters] - + def get_delay_data_registers(self, profile:Optional[int]=None, pack: bool=False, pack_single: bool=False) -> List[Dict[int,int]]: """ Get the delay data registers for all transmitters @@ -756,7 +756,7 @@ def get_delay_data_registers(self, profile:Optional[int]=None, pack: bool=False, if profile is None: profile = self.active_delay_profile return [tx.get_delay_data_registers(profile, pack=pack, pack_single=pack_single) for tx in self.transmitters] - + def get_pulse_data_registers(self, profile:Optional[int]=None, pack: bool=False, pack_single: bool=False) -> List[Dict[int,int]]: """ Get the pulse data registers for all transmitters @@ -767,7 +767,7 @@ def get_pulse_data_registers(self, profile:Optional[int]=None, pack: bool=False, if profile is None: profile = self.active_pulse_profile return [tx.get_pulse_data_registers(profile, pack=pack, pack_single=pack_single) for tx in self.transmitters] - + @dataclass class TxArray: i2c_addresses: Tuple[int] = (0x0,) @@ -804,11 +804,11 @@ def add_pulse_profile(self, pulse_profile: PulseProfile, activate: Optional[bool self.active_pulse_profile = pulse_profile.profile for module in self.modules.values(): module.add_pulse_profile(pulse_profile, activate) - + def add_delay_profile(self, delay_profile: DelayProfile, activate: Optional[bool]=None): """ Add a delay profile - + :param p: Delay profile :param activate: Activate the delay profile """ @@ -835,7 +835,7 @@ def add_delay_profile(self, delay_profile: DelayProfile, activate: Optional[bool def remove_pulse_profile(self, profile:int): """ Remove a pulse profile - + :param profile: Pulse profile number """ profiles = self.configured_pulse_profiles() @@ -863,7 +863,7 @@ def remove_delay_profile(self, profile:int): self.active_delay_profile = None for module in self.modules.values(): module.remove_delay_profile(profile) - + def get_pulse_profile(self, profile:Optional[int]=None) -> PulseProfile: """ Retrieve a pulse profile @@ -878,11 +878,11 @@ def get_pulse_profile(self, profile:Optional[int]=None) -> PulseProfile: raise ValueError(f"Pulse profile {profile} not found") i = profiles.index(profile) return self._pulse_profiles_list[i] - + def configured_pulse_profiles(self) -> List[int]: """ Get the configured pulse profiles - + :return: List of pulse profiles """ return [p.profile for p in self._pulse_profiles_list] @@ -901,11 +901,11 @@ def get_delay_profile(self, profile:Optional[int]=None) -> DelayProfile: raise ValueError(f"Delay profile {profile} not found") i = profiles.index(profile) return self._delay_profiles_list[i] - + def configured_delay_profiles(self) -> List[int]: """ Get the configured delay profiles - + :return: List of delay profiles """ return [p.profile for p in self._delay_profiles_list] @@ -913,21 +913,21 @@ def configured_delay_profiles(self) -> List[int]: def activate_pulse_profile(self, profile:int=1): """ Activates a pulse profile - + :param profile: Pulse profile number """ for module in self.modules.values(): module.activate_pulse_profile(profile) self.active_pulse_profile = profile - + def activate_delay_profile(self, profile:int=1): """ Activates a delay profile - + :param profile: Delay profile number """ for module in self.modules.values(): - module.activate_delay_profile(profile) + module.activate_delay_profile(profile) self.active_delay_profile = profile def recompute_pulse_profiles(self): @@ -964,7 +964,7 @@ def get_registers(self, profiles: ProfileOpts = "configured", recompute: bool = self.recompute_delay_profiles() self.recompute_pulse_profiles() return {addr:module.get_registers(profiles, pack=pack, pack_single=pack_single) for addr, module in self.modules.items()} - + def get_delay_control_registers(self, profile:Optional[int]=None) -> Dict[int, List[Dict[int,int]]]: """ Get the delay control registers for all modules @@ -975,7 +975,7 @@ def get_delay_control_registers(self, profile:Optional[int]=None) -> Dict[int, L if profile is None: profile = self.active_delay_profile return {addr:module.get_delay_control_registers(profile) for addr, module in self.modules.items()} - + def get_pulse_control_registers(self, profile:Optional[int]=None) -> Dict[int, List[Dict[int,int]]]: """ Get the pulse control registers for all modules @@ -986,7 +986,7 @@ def get_pulse_control_registers(self, profile:Optional[int]=None) -> Dict[int, L if profile is None: profile = self.active_pulse_profile return {addr:module.get_pulse_control_registers(profile) for addr, module in self.modules.items()} - + def get_delay_data_registers(self, profile:Optional[int]=None, pack: bool=False, pack_single: bool=False) -> Dict[int, List[Dict[int,int]]]: """ Get the delay data registers for all modules @@ -997,7 +997,7 @@ def get_delay_data_registers(self, profile:Optional[int]=None, pack: bool=False, if profile is None: profile = self.active_delay_profile return {addr:module.get_delay_data_registers(profile, pack=pack, pack_single=pack_single) for addr, module in self.modules.items()} - + def get_pulse_data_registers(self, profile:Optional[int]=None, pack: bool=False, pack_single: bool=False) -> Dict[int, List[Dict[int,int]]]: """ Get the pulse data registers for all modules @@ -1008,4 +1008,3 @@ def get_pulse_data_registers(self, profile:Optional[int]=None, pack: bool=False, if profile is None: profile = self.active_pulse_profile return {addr:module.get_pulse_data_registers(profile, pack=pack, pack_single=pack_single) for addr, module in self.modules.items()} - diff --git a/pyfus/plan/__init__.py b/src/openlifu/plan/__init__.py similarity index 100% rename from pyfus/plan/__init__.py rename to src/openlifu/plan/__init__.py diff --git a/pyfus/plan/protocol.py b/src/openlifu/plan/protocol.py similarity index 94% rename from pyfus/plan/protocol.py rename to src/openlifu/plan/protocol.py index e8314adb..8cc14877 100644 --- a/pyfus/plan/protocol.py +++ b/src/openlifu/plan/protocol.py @@ -1,6 +1,6 @@ from dataclasses import dataclass, field, InitVar from typing import List -from pyfus import bf, sim, seg, xdc, geo +from openlifu import bf, sim, seg, xdc, geo import json import xarray as xa @@ -18,7 +18,7 @@ class Protocol: seg_method: seg.SegmentationMethod = field(default_factory=seg.seg_methods.Water) param_constraints: dict = field(default_factory=dict) target_constraints: dict = field(default_factory=dict) - analysis_options: dict = field(default_factory=dict) + analysis_options: dict = field(default_factory=dict) @staticmethod def from_dict(d): @@ -31,7 +31,7 @@ def from_dict(d): seg_method_dict = d.get("seg_method", {}) if "materials" in d: seg_method_dict["materials"] = seg.Material.from_dict(d.pop("materials")) - d["seg_method"] = seg.SegmentationMethod.from_dict(seg_method_dict) + d["seg_method"] = seg.SegmentationMethod.from_dict(seg_method_dict) return Protocol(**d) def to_dict(self): @@ -50,14 +50,14 @@ def to_dict(self): "target_constraints": self.target_constraints, "analysis_options": self.analysis_options, } - + @staticmethod def from_file(filename): with open(filename, "r") as f: d = json.load(f) return Protocol.from_dict(d) - + def beamform(self, arr: xdc.Transducer, target:geo.Point, params: xa.Dataset): delays = self.delay_method.calc_delays(arr, target, params) apod = self.apod_method.calc_apodization(arr, target, params) - return delays, apod \ No newline at end of file + return delays, apod diff --git a/pyfus/plan/solution.py b/src/openlifu/plan/solution.py similarity index 76% rename from pyfus/plan/solution.py rename to src/openlifu/plan/solution.py index c39f4e96..9171a780 100644 --- a/pyfus/plan/solution.py +++ b/src/openlifu/plan/solution.py @@ -3,4 +3,4 @@ @dataclass class Solution: id: str = "solution" - name: str = "Solution" \ No newline at end of file + name: str = "Solution" diff --git a/pyfus/seg/__init__.py b/src/openlifu/seg/__init__.py similarity index 100% rename from pyfus/seg/__init__.py rename to src/openlifu/seg/__init__.py diff --git a/pyfus/seg/material.py b/src/openlifu/seg/material.py similarity index 96% rename from pyfus/seg/material.py rename to src/openlifu/seg/material.py index 64869894..6bb88e34 100644 --- a/pyfus/seg/material.py +++ b/src/openlifu/seg/material.py @@ -11,7 +11,7 @@ class Material: attenuation: float = 0.0 # dB/cm/MHz specific_heat: float = 4182.0 # J/kg/K thermal_conductivity: float = 0.598 # W/m/K - param_ids: Tuple[str] = field(default_factory= lambda: ("sound_speed", "density", "attenuation", "specific_heat", "thermal_conductivity"), init=False, repr=False) + param_ids: Tuple[str] = field(default_factory= lambda: ("sound_speed", "density", "attenuation", "specific_heat", "thermal_conductivity"), init=False, repr=False) @classmethod def param_info(cls, param_id: str): @@ -38,14 +38,14 @@ def get_param(self, param_id: str): if param_id not in self.param_ids: raise ValueError(f"Parameter {param_id} not found.") return self.__getattribute__(param_id) - + @staticmethod def get_materials(material_id="all", as_dict=True): material_id = ("water", "tissue", "skull", "air", "standoff") if material_id == "all" else material_id if isinstance(material_id, tuple) or isinstance(material_id, list): materials = {m: Material.get_materials(m, as_dict=False) for m in material_id} elif material_id in MATERIALS: - materials = MATERIALS[material_id] + materials = MATERIALS[material_id] else: raise ValueError(f"Material {material_id} not found.") if as_dict: @@ -109,4 +109,4 @@ def from_dict(d): "tissue": TISSUE, "skull": SKULL, "air": AIR, - "standoff": STANDOFF} \ No newline at end of file + "standoff": STANDOFF} diff --git a/pyfus/seg/seg_methods/__init__.py b/src/openlifu/seg/seg_methods/__init__.py similarity index 100% rename from pyfus/seg/seg_methods/__init__.py rename to src/openlifu/seg/seg_methods/__init__.py diff --git a/pyfus/seg/seg_methods/seg_method.py b/src/openlifu/seg/seg_methods/seg_method.py similarity index 88% rename from pyfus/seg/seg_methods/seg_method.py rename to src/openlifu/seg/seg_methods/seg_method.py index f241d734..4eeb84fb 100644 --- a/pyfus/seg/seg_methods/seg_method.py +++ b/src/openlifu/seg/seg_methods/seg_method.py @@ -1,16 +1,16 @@ from dataclasses import dataclass, field from abc import ABC, abstractmethod from typing import Optional -from pyfus.seg.material import Material, MATERIALS +from openlifu.seg.material import Material, MATERIALS import xarray as xa import numpy as np -from pyfus.seg import seg_methods +from openlifu.seg import seg_methods @dataclass class SegmentationMethod: materials: dict = field(default_factory=lambda: MATERIALS.copy()) ref_material: str = "water" - + def __post_init__(self): if self.ref_material not in self.materials.keys(): raise ValueError(f"Reference material {self.ref_material} not found.") @@ -21,13 +21,13 @@ def _segment(self, volume: xa.DataArray): @staticmethod def from_dict(d): if isinstance(d, str): - import pyfus.seg.seg_methods + import openlifu.seg.seg_methods if d == "water": - return pyfus.seg.seg_methods.Water() + return openlifu.seg.seg_methods.Water() elif d == "tissue": - return pyfus.seg.seg_methods.Tissue() + return openlifu.seg.seg_methods.Tissue() elif d == "segmented": - return pyfus.seg.seg_methods.SegmentMRI() + return openlifu.seg.seg_methods.SegmentMRI() else: d = d.copy() short_classname = d.pop("class") @@ -38,7 +38,7 @@ def from_dict(d): def _material_indices(self, materials: Optional[dict] = None): materials = self.materials if materials is None else materials return {material_id: i for i, material_id in enumerate(materials.keys())} - + def _map_params(self, seg: xa.DataArray, materials: Optional[dict] = None): materials = self.materials if materials is None else materials material_dict = self._material_indices(materials=materials) @@ -53,25 +53,25 @@ def _map_params(self, seg: xa.DataArray, materials: Optional[dict] = None): params[param_id] = param params.attrs['ref_material'] = ref_mat return params - + def seg_params(self, volume: xa.DataArray, materials: Optional[dict] = None): materials = self.materials if materials is None else materials seg = self._segment(volume) params = self._map_params(seg, materials=materials) return params - + def ref_params(self, coords: xa.Coordinates): seg = self._ref_segment(coords) params = self._map_params(seg) return params - + def _ref_segment(self, coords: xa.Coordinates): material_dict = self._material_indices() m_idx = material_dict[self.ref_material] sz = list(coords.sizes.values()) seg = xa.DataArray(np.full(sz, m_idx, dtype=int), coords=coords) return seg - + def to_dict(self): d = self.__dict__.copy() d['class'] = self.__class__.__name__ @@ -80,4 +80,4 @@ def to_dict(self): @dataclass class UniformSegmentation(SegmentationMethod): def _segment(self, vol: xa.DataArray): - return self._ref_segment(vol.coords) \ No newline at end of file + return self._ref_segment(vol.coords) diff --git a/pyfus/seg/seg_methods/segment_mri.py b/src/openlifu/seg/seg_methods/segment_mri.py similarity index 59% rename from pyfus/seg/seg_methods/segment_mri.py rename to src/openlifu/seg/seg_methods/segment_mri.py index 8775ddbd..1f5c5b85 100644 --- a/pyfus/seg/seg_methods/segment_mri.py +++ b/src/openlifu/seg/seg_methods/segment_mri.py @@ -1,8 +1,8 @@ from dataclasses import dataclass -from pyfus.seg.seg_methods.seg_method import SegmentationMethod +from openlifu.seg.seg_methods.seg_method import SegmentationMethod import xarray as xa @dataclass class SegmentMRI(SegmentationMethod): def _segment(self, volume: xa.DataArray): - raise NotImplementedError \ No newline at end of file + raise NotImplementedError diff --git a/src/openlifu/seg/seg_methods/tissue.py b/src/openlifu/seg/seg_methods/tissue.py new file mode 100644 index 00000000..1423fbf1 --- /dev/null +++ b/src/openlifu/seg/seg_methods/tissue.py @@ -0,0 +1,6 @@ +from dataclasses import dataclass +from openlifu.seg.seg_methods.seg_method import UniformSegmentation + +@dataclass +class Tissue(UniformSegmentation): + ref_material: str = "tissue" diff --git a/src/openlifu/seg/seg_methods/water.py b/src/openlifu/seg/seg_methods/water.py new file mode 100644 index 00000000..fdff9ece --- /dev/null +++ b/src/openlifu/seg/seg_methods/water.py @@ -0,0 +1,6 @@ +from dataclasses import dataclass +from openlifu.seg.seg_methods.seg_method import UniformSegmentation + +@dataclass +class Water(UniformSegmentation): + ref_material: str = "water" diff --git a/pyfus/sim/__init__.py b/src/openlifu/sim/__init__.py similarity index 100% rename from pyfus/sim/__init__.py rename to src/openlifu/sim/__init__.py diff --git a/pyfus/sim/kwave_if.py b/src/openlifu/sim/kwave_if.py similarity index 91% rename from pyfus/sim/kwave_if.py rename to src/openlifu/sim/kwave_if.py index 9b1dab28..2310f8e5 100644 --- a/pyfus/sim/kwave_if.py +++ b/src/openlifu/sim/kwave_if.py @@ -1,5 +1,5 @@ -from pyfus import xdc -from pyfus.util.units import getunitconversion +from openlifu import xdc +from openlifu.util.units import getunitconversion import kwave import kwave.data from kwave.kgrid import kWaveGrid @@ -29,7 +29,7 @@ def get_kgrid(coords: xa.Coordinates, t_end = 0, dt = 0, sound_speed_ref=1500): kgrid.setTime(Nt, dt) return kgrid -def get_karray(arr: xdc.Transducer, +def get_karray(arr: xdc.Transducer, bli_tolerance: float = 0.05, upsampling_rate: int = 5, translation: List[float] = [0.,0.,0.], @@ -47,7 +47,7 @@ def get_karray(arr: xdc.Transducer, return karray def get_medium(params: xa.Dataset): - medium= kWaveMedium(sound_speed=params['sound_speed'].attrs['ref_value'], + medium= kWaveMedium(sound_speed=params['sound_speed'].attrs['ref_value'], density=params['density'].attrs['ref_value']) return medium @@ -73,10 +73,10 @@ def hash_array_kgrid(kgrid, karray): 'BLI_tolerance': karray.bli_tolerance, 'upsampling_rate': karray.upsampling_rate} check = c.checksum(bytes(json.dumps(d), 'utf-8')) - return f'{check:x}' + return f'{check:x}' -def run_simulation(arr: xdc.Transducer, - params: xa.Dataset, +def run_simulation(arr: xdc.Transducer, + params: xa.Dataset, delays: Optional[np.ndarray] = None, apod: Optional[np.ndarray] = None, freq: float = 1e6, @@ -100,7 +100,7 @@ def run_simulation(arr: xdc.Transducer, pcoords = params.coords['lat'].attrs['units'] scl = getunitconversion(pcoords, 'm') array_offset =[-float(coord.mean())*scl for coord in params.coords.values()] - karray = get_karray(arr, + karray = get_karray(arr, translation=array_offset, bli_tolerance=bli_tolerance, upsampling_rate=upsampling_rate) @@ -127,26 +127,26 @@ def run_simulation(arr: xdc.Transducer, data_cast='single' ) execution_options = SimulationExecutionOptions(is_gpu_simulation=True) - output = kspaceFirstOrder3D(kgrid=kgrid, - source=source, - sensor=sensor, - medium=medium, + output = kspaceFirstOrder3D(kgrid=kgrid, + source=source, + sensor=sensor, + medium=medium, simulation_options=simulation_options, execution_options=execution_options) logging.info('Simulation Complete') sz = list(params.coords.sizes.values()) p_max = xa.DataArray(output['p_max'].reshape(sz, order='F'), coords=params.coords, - name='p_max', + name='p_max', attrs={'units':'Pa', 'long_name':'PPP'}) p_min = xa.DataArray(-1*output['p_min'].reshape(sz, order='F'), coords=params.coords, - name='p_min', + name='p_min', attrs={'units':'Pa', 'long_name':'PNP'}) Z = params['density'].data*params['sound_speed'].data intensity = xa.DataArray(1e-4*output['p_min'].reshape(sz, order='F')**2/(2*Z), coords=params.coords, - name='I', + name='I', attrs={'units':'W/cm^2', 'long_name':'Intensity'}) ds = xa.Dataset({'p_max':p_max, 'p_min':p_min, 'ita':intensity}) return ds, output diff --git a/pyfus/sim/sim_setup.py b/src/openlifu/sim/sim_setup.py similarity index 95% rename from pyfus/sim/sim_setup.py rename to src/openlifu/sim/sim_setup.py index 21a0b130..b4be6204 100644 --- a/pyfus/sim/sim_setup.py +++ b/src/openlifu/sim/sim_setup.py @@ -3,8 +3,8 @@ import numpy as np import xarray as xa import logging -from pyfus.util.units import getunitconversion -from pyfus.xdc import Transducer +from openlifu.util.units import getunitconversion +from openlifu.xdc import Transducer @dataclass class SimSetup: @@ -58,24 +58,24 @@ def get_coords(self, dims=None, units: Optional[str] = None): coords = xa.Coordinates({dim: np.linspace(extents[i][0], extents[i][1], sizes[i]) for i, dim in enumerate(dims)}) for i, dim in enumerate(dims): coords[dim].attrs['units'] = units - coords[dim].attrs['long_name'] = self.names[i] + coords[dim].attrs['long_name'] = self.names[i] return coords - + def get_corners(self, id: str = "corners", units: Optional[str] = None): units = self.units if units is None else units scl = getunitconversion(self.units, units) xyz = np.array(np.meshgrid(self.x_extent, self.y_extent, self.z_extent, indexing='ij')) corners = xyz.reshape(3,-1) return corners*scl - + def get_extent(self, dims: Optional[str]=None, units: Optional[str] = None): dims = self.dims if dims is None else dims units = self.units if units is None else units scl = getunitconversion(self.units, units) extents = [self.x_extent, self.y_extent, self.z_extent] - return np.array([extents[self.dims.index(dim)] for dim in dims])*scl - - def get_max_cycle_offset(self, arr:Transducer, frequency: Optional[float] = None, delays: Optional[np.ndarray]=None, zmin: float =10e-3): + return np.array([extents[self.dims.index(dim)] for dim in dims])*scl + + def get_max_cycle_offset(self, arr:Transducer, frequency: Optional[float] = None, delays: Optional[np.ndarray]=None, zmin: float =10e-3): frequency = arr.frequency if frequency is None else frequency delays = np.zeros(arr.numelements()) if delays is None else delays coords = self.get_coords(units="m") @@ -93,19 +93,19 @@ def get_max_distance(self, arr: Transducer, units: Optional[str] = None): distances = np.array([[el.distance_to_point(corner, units=units) for corner in corners.T] for el in arr.elements]) max_distance = np.max(distances) return max_distance - + def get_size(self, dims: Optional[str]=None): dims = self.dims if dims is None else dims n = [int(np.round(np.diff(ext)/self.spacing))+1 for ext in [self.x_extent, self.y_extent, self.z_extent]] return np.array([n[self.dims.index(dim)] for dim in dims]).squeeze() - + def get_spacing(self, units: Optional[str] = None): units = self.units if units is None else units return getunitconversion(self.units, units)*self.spacing def transform_scene(self, scene, id: Optional[str] = None, name: Optional[str] = None, units: Optional[str] = None): raise NotImplementedError - + @staticmethod def from_dict(d): - return SimSetup(**d) \ No newline at end of file + return SimSetup(**d) diff --git a/pyfus/util/json.py b/src/openlifu/util/json.py similarity index 76% rename from pyfus/util/json.py rename to src/openlifu/util/json.py index 4ad008f1..170444d6 100644 --- a/pyfus/util/json.py +++ b/src/openlifu/util/json.py @@ -1,12 +1,12 @@ import json import os import numpy as np -from pyfus.geo import Point -from pyfus.xdc.transducer import Transducer -from pyfus.xdc.element import Element -from pyfus.seg.material import Material -from pyfus.db.session import Session -from pyfus.db.subject import Subject +from openlifu.geo import Point +from openlifu.xdc.transducer import Transducer +from openlifu.xdc.element import Element +from openlifu.seg.material import Material +from openlifu.db.session import Session +from openlifu.db.subject import Subject class PYFUSEncoder(json.JSONEncoder): def default(self, obj): @@ -35,4 +35,4 @@ def to_json(obj, filename): if dirname and not os.path.exists(dirname): os.makedirs(dirname) with open(filename, 'w') as file: - json.dump(obj, file, cls=PYFUSEncoder, indent=4) \ No newline at end of file + json.dump(obj, file, cls=PYFUSEncoder, indent=4) diff --git a/pyfus/util/strings.py b/src/openlifu/util/strings.py similarity index 98% rename from pyfus/util/strings.py rename to src/openlifu/util/strings.py index 3ee533d9..9f8a2488 100644 --- a/pyfus/util/strings.py +++ b/src/openlifu/util/strings.py @@ -36,4 +36,4 @@ def sanitize(in_str, case: Cases ='snake'): else: raise ValueError(f'Unrecognized case type {case}') - return out_str \ No newline at end of file + return out_str diff --git a/pyfus/util/units.py b/src/openlifu/util/units.py similarity index 97% rename from pyfus/util/units.py rename to src/openlifu/util/units.py index 6f95f1cd..5bb3abf0 100644 --- a/pyfus/util/units.py +++ b/src/openlifu/util/units.py @@ -28,17 +28,17 @@ def getunittype(unit): def getunitconversion(from_unit, to_unit, unitratio=None, constant=None): if not from_unit: return 1.0 - + if unitratio is not None and constant is not None: if '/' not in unitratio: raise ValueError('Conversion unit ratio must have a \'/\' symbol') - + unitn, unitd = unitratio.split('/') type0 = getunittype(from_unit) type1 = getunittype(to_unit) typen = getunittype(unitn) typed = getunittype(unitd) - + if type0 == typed and type1 == typen: scl = getunitconversion(from_unit, unitd) * constant * getunitconversion(unitn, to_unit) elif type0 == typen and type1 == typed: @@ -50,7 +50,7 @@ def getunitconversion(from_unit, to_unit, unitratio=None, constant=None): else: slash0 = from_unit.find('/') slash1 = to_unit.find('/') - + if slash0 != -1 and slash1 != -1: num0 = from_unit[:slash0] denom0 = from_unit[slash0+1:] @@ -60,19 +60,19 @@ def getunitconversion(from_unit, to_unit, unitratio=None, constant=None): elif slash0 == -1 and slash1 == -1: type0 = getunittype(from_unit) type1 = getunittype(to_unit) - + if type0 != type1: raise ValueError('Unit type mismatch ({}) vs ({})'.format(type0, type1)) - + if type0 == 'other': if from_unit[-1] != to_unit[-1]: raise ValueError('Cannot convert {} to {}'.format(from_unit, to_unit)) - + i = 0 while i < min(len(from_unit), len(to_unit)) and from_unit[-i:] == to_unit[-i:]: type = from_unit[-i:] i += 1 - + scl0 = getsiscale(from_unit, type) scl1 = getsiscale(to_unit, type) scl = scl0 / scl1 @@ -82,12 +82,12 @@ def getunitconversion(from_unit, to_unit, unitratio=None, constant=None): scl = scl0 / scl1 else: raise ValueError('Unit ratio mismatch ({} vs {})'.format(from_unit, to_unit)) - + return scl def getsiscale(unit, type): type = type.lower() - + if type in ['distance', 'area', 'volume']: idx = unit.find('meters') if idx == -1: @@ -99,7 +99,7 @@ def getsiscale(unit, type): idx = unit.rfind('m') if idx == -1: idx = len(unit) - + elif type == 'time': idx = unit.find('seconds') if idx == -1: @@ -110,25 +110,25 @@ def getsiscale(unit, type): idx = unit.rfind('s') if idx == -1: idx = len(unit) - + elif type == 'angle': idx = len(unit) - + elif type == 'frequency': idx = len(unit) - 1 - + else: idx = len(unit) - len(type) + 1 - + idx = idx prefix = unit[:idx] - + if not prefix: scl = 1.0 else: prefix = prefix.lower() scl = 1.0 - + if prefix == 'pico' or prefix == 'p': scl = 1.0e-12 elif prefix == 'nano' or prefix == 'n': @@ -162,10 +162,10 @@ def getsiscale(unit, type): else: if prefix: raise ValueError('Unknown prefix {}'.format(prefix)) - + if type == 'area': scl = scl ** 2.0 elif type == 'volume': scl = scl ** 3.0 - + return scl diff --git a/pyfus/xdc/__init__.py b/src/openlifu/xdc/__init__.py similarity index 68% rename from pyfus/xdc/__init__.py rename to src/openlifu/xdc/__init__.py index 70fdfd99..fd1bd724 100644 --- a/pyfus/xdc/__init__.py +++ b/src/openlifu/xdc/__init__.py @@ -1,4 +1,4 @@ from . import element from . import transducer from .element import Element -from .transducer import Transducer \ No newline at end of file +from .transducer import Transducer diff --git a/pyfus/xdc/element.py b/src/openlifu/xdc/element.py similarity index 93% rename from pyfus/xdc/element.py rename to src/openlifu/xdc/element.py index 8f86aa6f..0e7fa70c 100644 --- a/pyfus/xdc/element.py +++ b/src/openlifu/xdc/element.py @@ -1,7 +1,7 @@ import numpy as np -from pyfus.util.units import getunitconversion +from openlifu.util.units import getunitconversion from dataclasses import dataclass, field -from collections.abc import Iterable +from collections.abc import Iterable from typing import List, Dict, Any, Tuple import copy @@ -163,7 +163,7 @@ def angle_to_point(self, point, units=None, return_as="rad", matrix=np.eye(4)): if return_as == "deg": theta = np.degrees(theta) return theta - + def set_matrix(self, matrix, units=None): if units is not None: self.rescale(units) @@ -176,18 +176,18 @@ def set_matrix(self, matrix, units=None): self.roll = roll def to_dict(self): - return {"index": self.index, - "x": self.x, - "y": self.y, - "z": self.z, - "az": self.az, - "el": self.el, - "roll": self.roll, - "w": self.w, - "l": self.l, - "impulse_response": self.impulse_response.tolist(), - "impulse_dt": self.impulse_dt, - "pin": self.pin, + return {"index": self.index, + "x": self.x, + "y": self.y, + "z": self.z, + "az": self.az, + "el": self.el, + "roll": self.roll, + "w": self.w, + "l": self.l, + "impulse_response": self.impulse_response.tolist(), + "impulse_dt": self.impulse_dt, + "pin": self.pin, "units": self.units} @staticmethod diff --git a/pyfus/xdc/transducer.py b/src/openlifu/xdc/transducer.py similarity index 96% rename from pyfus/xdc/transducer.py rename to src/openlifu/xdc/transducer.py index 5c31a051..88f3e395 100644 --- a/pyfus/xdc/transducer.py +++ b/src/openlifu/xdc/transducer.py @@ -1,9 +1,9 @@ import numpy as np import pandas as pd -from pyfus.util.units import getunitconversion +from openlifu.util.units import getunitconversion from dataclasses import dataclass, field -from collections.abc import Iterable +from collections.abc import Iterable from typing import List, Dict, Any, Tuple import vtk import logging @@ -28,7 +28,7 @@ def __post_init__(self): self.matrix = np.array(self.matrix, dtype=np.float64) for element in self.elements: element.rescale(self.units) - + def calc_output(self, input_signal, dt, delays: np.ndarray = None, apod: np.ndarray = None): if delays is None: delays = np.zeros(self.numelements()) @@ -40,14 +40,14 @@ def calc_output(self, input_signal, dt, delays: np.ndarray = None, apod: np.ndar for i, o in enumerate(outputs): output_signal[i, :len(o)] = o return output_signal - + def copy(self): return copy.deepcopy(self) - def draw(self, - units=None, - transform=True, - facecolor=[0,1,1], + def draw(self, + units=None, + transform=True, + facecolor=[0,1,1], facealpha=0.5): units = self.units if units is None else units actor = self.get_actor(units=units, transform=transform, facecolor=facecolor, facealpha=facealpha) @@ -116,7 +116,7 @@ def get_corners(self, transform=True, units=None): else: matrix = np.eye(4) return [element.get_corners(units=units, matrix=matrix) for element in self.elements] - + def get_positions(self, transform=True, units=None): units = self.units if units is None else units if transform: @@ -154,7 +154,7 @@ def merge(list_of_transducers): xform_array.transform(np.dot(np.linalg.inv(arr.get_matrix()),ref_matrix), transform_elements=True) merged_array.elements += xform_array.elements return merged_array - + def numelements(self): return len(self.elements) @@ -165,15 +165,15 @@ def rescale(self, units): scl = getunitconversion(self.units, units) self.matrix[0:3, 3] *= scl self.units = units - + def to_dict(self): d = self.__dict__.copy() d["elements"] = [element.to_dict() for element in d["elements"]] d["matrix"] = d["matrix"].tolist() return d - + def to_file(self, filename): - from pyfus.util.json import to_json + from openlifu.util.json import to_json to_json(self.to_dict(), filename) def transform(self, matrix, units=None, transform_elements: bool=False): @@ -221,6 +221,3 @@ def gen_matrix_array(nx=2, ny=2, pitch=1, kerf=0, units="mm", impulse_response=1 units=units )) return Transducer(elements=elements, id=id, name=name, attrs=attrs) - - -