Module nlisim.geometry.generator

Expand source code
import json
import struct
import time
from typing import List, Tuple, Union

import h5py
import numpy as np
from scipy import ndimage
import vtk

from nlisim.coordinates import Point
from nlisim.diffusion import discrete_laplacian
from nlisim.geometry.math_function import Cylinder, Sphere
from nlisim.grid import RectangularGrid

# tissue type
SAC = 'sac'
DUCT = 'duct'
QUADRIC = 'quadric'

# tissue number
AIR = 0
BLOOD = 1
REGULAR_TISSUE = 2
EPITHELIUM = 3
SURF = 4
PORES = 5

ShapeType = Tuple[int, int, int]
SpacingType = Tuple[float, float, float]


class Geometry(object):
    def __init__(self, shape: ShapeType, space: SpacingType, scale: int, randomness: int):
        self.scale = scale
        self.randomness = randomness
        self.shape = (shape[0] * scale, shape[1] * scale, shape[2] * scale)
        self.space = space
        self.grid = RectangularGrid.construct_uniform(self.shape, self.space)

        self.geo = self.grid.allocate_variable(dtype=np.dtype(np.int8))
        self.geo.fill(2)
        self.fixed = np.zeros(self.shape)

        self.duct_f: List[Union[Sphere, Cylinder]] = []
        self.sac_f: List[Union[Sphere, Cylinder]] = []

    def add(self, function):
        """Add functions to the generator."""
        function.scale(self.scale)
        if function.type == SAC:
            self.sac_f.append(function)
        elif function.type == DUCT:
            self.duct_f.append(function)
        else:
            raise Exception('Unknown tissue type')

    def construct_sphere(self, lungtissue, center: Point, r: float):
        """Construct sphere within simulation space."""
        coords = np.ogrid[: lungtissue.shape[0], : lungtissue.shape[1], : lungtissue.shape[2]]
        distance = np.sqrt(
            (coords[0] - center.z) ** 2 + (coords[1] - center.y) ** 2 + (coords[2] - center.x) ** 2
        )
        return 1 * (distance <= r)

    def construct_cylinder(
        self,
        lung_tissue: np.ndarray,
        center: Point,
        length: float,
        direction: np.ndarray,
        r: np.ndarray,
    ):
        """Construct cylinder within simulation space."""
        coords = np.indices(lung_tissue.shape, dtype=np.float64).T

        # normalize direction, just in case
        direction = direction / np.linalg.norm(direction)

        relative_coords: np.ndarray = coords - center
        distance_along_axis: np.ndarray = relative_coords @ direction
        distance_from_axis = np.linalg.norm(
            relative_coords - np.multiply.outer(relative_coords @ direction, direction), axis=3
        )
        mask = np.logical_and(distance_from_axis <= r.T, distance_along_axis <= (length / 2.0)).T
        return mask

    def construct_air_duct(self, random_mask):
        print('constructing air duct...')
        tissue = self.geo
        fixed = self.fixed
        # construct air duct
        for function in self.duct_f:
            if isinstance(function, Cylinder):
                air_mask = self.construct_cylinder(
                    tissue,
                    function.center,
                    function.length,
                    function.direction,
                    function.radius + random_mask,
                )
                tissue[np.logical_and(air_mask == 1, fixed == 0)] = AIR

        # blur the noise to maintain the continuousness of the air
        blur_mask = np.where(tissue == AIR, 1, 0)
        blur_air_mask = ndimage.filters.convolve(blur_mask, np.ones((3, 3, 3)))
        tissue[blur_air_mask > 13] = AIR
        tissue[blur_air_mask <= 13] = REGULAR_TISSUE
        fixed[tissue == AIR] = 1

        # construct epithelium layer
        air_mask = np.where(tissue == AIR, 1, 0)
        epithelium_mask = ndimage.filters.convolve(air_mask, np.ones((3, 3, 3)))
        tissue[np.logical_and(epithelium_mask > 0, tissue == REGULAR_TISSUE)] = EPITHELIUM

    def construct_alveolus(self, random_mask):
        tissue = self.geo
        fixed = self.fixed
        print('constructing alveolus...')
        # construct sac
        for function in self.sac_f:
            if isinstance(function, Sphere):
                air_mask = self.construct_sphere(
                    tissue, function.center, function.radius + random_mask
                )
                blur_air_mask = ndimage.filters.convolve(air_mask, np.ones((3, 3, 3)))
                fixed_air_mask = np.logical_and(blur_air_mask > 13, fixed == 0)
                tissue[fixed_air_mask] = AIR
                tissue[
                    np.logical_and(np.logical_and(blur_air_mask <= 13, fixed == 0), tissue == AIR)
                ] = REGULAR_TISSUE
                fixed[fixed_air_mask] = 1

                # construct epithelium layer
                epithelium_mask = ndimage.filters.convolve(fixed_air_mask, np.ones((3, 3, 3)))
                fixed_epithelium_mask = np.logical_and(epithelium_mask > 0, fixed == 0)
                tissue[fixed_epithelium_mask] = EPITHELIUM
                fixed[fixed_epithelium_mask] = 1

    def construct(self, simple):
        """Construct the simulation space with math functions."""
        tissue = self.geo
        # fixed = self.fixed

        random_mask = np.random.normal(0, self.randomness, self.shape)

        self.construct_air_duct(random_mask)
        self.construct_alveolus(random_mask)

        epi_mask = np.where(tissue == EPITHELIUM, 2, 0)
        surf_mask = ndimage.filters.convolve(epi_mask, np.ones((3, 3, 3)))
        print('constructing surfactant layer and capillary...')
        # construct surfactant and blood vessel
        if not simple:
            tissue[np.logical_and(tissue == AIR, surf_mask > 0)] = SURF
        tissue[np.logical_and(tissue == REGULAR_TISSUE, surf_mask > 0)] = BLOOD

    def write_to_vtk(self, filename):
        zbin, ybin, xbin = self.shape

        f = open(filename, 'w')
        f.write('# vtk DataFile Version 4.2\n')
        f.write('Aspergillus simulation: Geometry\n')
        f.write('BINARY\n')
        f.write('DATASET STRUCTURED_POINTS\n')
        f.write('DIMENSIONS ' + str(xbin) + ' ' + str(ybin) + ' ' + str(zbin) + '\n')
        f.write('ASPECT_RATIO 1 1 1\n')
        f.write('ORIGIN 0 0 0\n')
        f.write('POINT_DATA ' + str(xbin * ybin * zbin) + '\n')
        f.write('SCALARS TissueType unsigned_char 1\n')
        f.write('LOOKUP_TABLE default\n')
        f.close()

        f = open(filename, 'ab')
        array = self.geo.flatten()
        array = array.astype(int)

        b = struct.pack(len(array) * 'B', *array)
        f.write(b)
        f.close()

    def write_to_hdf5(self, filename, laplacian):
        # Write data to HDF5
        with h5py.File(filename, 'w') as data_file:
            data_file.create_dataset('geometry', data=self.geo)

            if laplacian:
                # embed laplacian matrix for all layers
                # surfactant layer laplacian
                surf_lapl = discrete_laplacian(self.grid, self.geo == SURF)
                # epithelium layer laplacian
                epi_lapl = discrete_laplacian(self.grid, self.geo == EPITHELIUM)
                # capillary layer laplacian
                blood_lapl = discrete_laplacian(self.grid, self.geo == BLOOD)

                d = {'surf_lapl': surf_lapl, 'epi_lapl': epi_lapl, 'blood_lapl': blood_lapl}
                matrices = data_file.create_group('lapl_matrices')
                for name, lapl in d.items():
                    matrix = matrices.create_group(name)
                    matrix.create_dataset('data', data=lapl.data)
                    matrix.create_dataset('indptr', data=lapl.indptr)
                    matrix.create_dataset('indices', data=lapl.indices)
                    matrix.attrs['shape'] = lapl.shape

    def preview(self):
        zbin, ybin, xbin = self.shape
        data_importer = vtk.vtkImageImport()

        g = self.geo.flatten()
        g = np.uint8(g)
        data_string = g.tostring()
        data_importer.CopyImportVoidPointer(data_string, len(data_string))
        data_importer.SetDataScalarTypeToUnsignedChar()
        data_importer.SetNumberOfScalarComponents(1)

        data_importer.SetDataExtent(0, xbin - 1, 0, ybin - 1, 0, zbin - 1)
        data_importer.SetWholeExtent(0, xbin - 1, 0, ybin - 1, 0, zbin - 1)

        # Create transfer mapping scalar value to opacity
        opacity_transfer_function = vtk.vtkPiecewiseFunction()
        opacity_transfer_function.AddPoint(0, 0.0)
        opacity_transfer_function.AddPoint(1, 0.2)
        opacity_transfer_function.AddPoint(2, 0.005)
        opacity_transfer_function.AddPoint(3, 1)

        # Create transfer mapping scalar value to color
        color_transfer_function = vtk.vtkColorTransferFunction()
        color_transfer_function.AddRGBPoint(0, 0.0, 0.0, 1.0)
        color_transfer_function.AddRGBPoint(1, 1.0, 0.0, 0.0)
        color_transfer_function.AddRGBPoint(2, 0.0, 0.0, 1.0)
        color_transfer_function.AddRGBPoint(3, 1.0, 1.0, 1.0)

        # The property describes how the data will look
        volume_property = vtk.vtkVolumeProperty()
        volume_property.SetColor(color_transfer_function)
        volume_property.SetScalarOpacity(opacity_transfer_function)
        volume_property.SetInterpolationTypeToLinear()

        # The mapper / ray cast function know how to render the data
        volume_mapper = vtk.vtkGPUVolumeRayCastMapper()
        volume_mapper.SetBlendModeToComposite()
        volume_mapper.SetInputConnection(data_importer.GetOutputPort())

        # The volume holds the mapper and the property and
        # can be used to position/orient the volume
        volume = vtk.vtkVolume()
        volume.SetMapper(volume_mapper)
        volume.SetProperty(volume_property)

        ren = vtk.vtkRenderer()
        ren_win = vtk.vtkRenderWindow()
        ren_win.AddRenderer(ren)
        iren = vtk.vtkRenderWindowInteractor()
        iren.SetRenderWindow(ren_win)

        ren.AddVolume(volume)
        ren.SetBackground(1, 1, 1)
        ren_win.SetSize(600, 600)
        ren_win.Render()

        def check_abort(obj, event):
            if obj.GetEventPending() != 0:
                obj.SetAbortRender(1)

        ren_win.AddObserver('AbortCheckEvent', check_abort)

        iren.Initialize()
        ren_win.Render()
        iren.Start()


def generate_geometry(config, output, preview, simple, lapl):
    start_time = time.time()

    with open(config) as f:
        data = json.load(f)

        scale = data['scaling']
        randomness = data['randomness']
        shape = (data['shape']['zbin'], data['shape']['ybin'], data['shape']['xbin'])
        space = (data['space']['dz'], data['space']['dy'], data['space']['dx'])

        g = Geometry(shape, space, scale, randomness)

        for function in data['function']:
            if function['shape'] == 'sphere':
                f = Sphere(function['center'], function['radius'], function['type'])
                g.add(f)

            elif function['shape'] == 'cylinder':
                f = Cylinder(
                    function['center'],
                    function['direction'],
                    function['radius'],
                    function['length'],
                    function['type'],
                )
                g.add(f)

        # g.scaling(data["scaling"])
        g.construct(simple)
        g.write_to_hdf5(output + '.hdf5', lapl)
        g.write_to_vtk(output + '.vtk')
    print(f'--- {(time.time() - start_time)} seconds ---')
    if preview:
        g.preview()

Functions

def generate_geometry(config, output, preview, simple, lapl)
Expand source code
def generate_geometry(config, output, preview, simple, lapl):
    start_time = time.time()

    with open(config) as f:
        data = json.load(f)

        scale = data['scaling']
        randomness = data['randomness']
        shape = (data['shape']['zbin'], data['shape']['ybin'], data['shape']['xbin'])
        space = (data['space']['dz'], data['space']['dy'], data['space']['dx'])

        g = Geometry(shape, space, scale, randomness)

        for function in data['function']:
            if function['shape'] == 'sphere':
                f = Sphere(function['center'], function['radius'], function['type'])
                g.add(f)

            elif function['shape'] == 'cylinder':
                f = Cylinder(
                    function['center'],
                    function['direction'],
                    function['radius'],
                    function['length'],
                    function['type'],
                )
                g.add(f)

        # g.scaling(data["scaling"])
        g.construct(simple)
        g.write_to_hdf5(output + '.hdf5', lapl)
        g.write_to_vtk(output + '.vtk')
    print(f'--- {(time.time() - start_time)} seconds ---')
    if preview:
        g.preview()

Classes

class Geometry (shape: Tuple[int, int, int], space: Tuple[float, float, float], scale: int, randomness: int)
Expand source code
class Geometry(object):
    def __init__(self, shape: ShapeType, space: SpacingType, scale: int, randomness: int):
        self.scale = scale
        self.randomness = randomness
        self.shape = (shape[0] * scale, shape[1] * scale, shape[2] * scale)
        self.space = space
        self.grid = RectangularGrid.construct_uniform(self.shape, self.space)

        self.geo = self.grid.allocate_variable(dtype=np.dtype(np.int8))
        self.geo.fill(2)
        self.fixed = np.zeros(self.shape)

        self.duct_f: List[Union[Sphere, Cylinder]] = []
        self.sac_f: List[Union[Sphere, Cylinder]] = []

    def add(self, function):
        """Add functions to the generator."""
        function.scale(self.scale)
        if function.type == SAC:
            self.sac_f.append(function)
        elif function.type == DUCT:
            self.duct_f.append(function)
        else:
            raise Exception('Unknown tissue type')

    def construct_sphere(self, lungtissue, center: Point, r: float):
        """Construct sphere within simulation space."""
        coords = np.ogrid[: lungtissue.shape[0], : lungtissue.shape[1], : lungtissue.shape[2]]
        distance = np.sqrt(
            (coords[0] - center.z) ** 2 + (coords[1] - center.y) ** 2 + (coords[2] - center.x) ** 2
        )
        return 1 * (distance <= r)

    def construct_cylinder(
        self,
        lung_tissue: np.ndarray,
        center: Point,
        length: float,
        direction: np.ndarray,
        r: np.ndarray,
    ):
        """Construct cylinder within simulation space."""
        coords = np.indices(lung_tissue.shape, dtype=np.float64).T

        # normalize direction, just in case
        direction = direction / np.linalg.norm(direction)

        relative_coords: np.ndarray = coords - center
        distance_along_axis: np.ndarray = relative_coords @ direction
        distance_from_axis = np.linalg.norm(
            relative_coords - np.multiply.outer(relative_coords @ direction, direction), axis=3
        )
        mask = np.logical_and(distance_from_axis <= r.T, distance_along_axis <= (length / 2.0)).T
        return mask

    def construct_air_duct(self, random_mask):
        print('constructing air duct...')
        tissue = self.geo
        fixed = self.fixed
        # construct air duct
        for function in self.duct_f:
            if isinstance(function, Cylinder):
                air_mask = self.construct_cylinder(
                    tissue,
                    function.center,
                    function.length,
                    function.direction,
                    function.radius + random_mask,
                )
                tissue[np.logical_and(air_mask == 1, fixed == 0)] = AIR

        # blur the noise to maintain the continuousness of the air
        blur_mask = np.where(tissue == AIR, 1, 0)
        blur_air_mask = ndimage.filters.convolve(blur_mask, np.ones((3, 3, 3)))
        tissue[blur_air_mask > 13] = AIR
        tissue[blur_air_mask <= 13] = REGULAR_TISSUE
        fixed[tissue == AIR] = 1

        # construct epithelium layer
        air_mask = np.where(tissue == AIR, 1, 0)
        epithelium_mask = ndimage.filters.convolve(air_mask, np.ones((3, 3, 3)))
        tissue[np.logical_and(epithelium_mask > 0, tissue == REGULAR_TISSUE)] = EPITHELIUM

    def construct_alveolus(self, random_mask):
        tissue = self.geo
        fixed = self.fixed
        print('constructing alveolus...')
        # construct sac
        for function in self.sac_f:
            if isinstance(function, Sphere):
                air_mask = self.construct_sphere(
                    tissue, function.center, function.radius + random_mask
                )
                blur_air_mask = ndimage.filters.convolve(air_mask, np.ones((3, 3, 3)))
                fixed_air_mask = np.logical_and(blur_air_mask > 13, fixed == 0)
                tissue[fixed_air_mask] = AIR
                tissue[
                    np.logical_and(np.logical_and(blur_air_mask <= 13, fixed == 0), tissue == AIR)
                ] = REGULAR_TISSUE
                fixed[fixed_air_mask] = 1

                # construct epithelium layer
                epithelium_mask = ndimage.filters.convolve(fixed_air_mask, np.ones((3, 3, 3)))
                fixed_epithelium_mask = np.logical_and(epithelium_mask > 0, fixed == 0)
                tissue[fixed_epithelium_mask] = EPITHELIUM
                fixed[fixed_epithelium_mask] = 1

    def construct(self, simple):
        """Construct the simulation space with math functions."""
        tissue = self.geo
        # fixed = self.fixed

        random_mask = np.random.normal(0, self.randomness, self.shape)

        self.construct_air_duct(random_mask)
        self.construct_alveolus(random_mask)

        epi_mask = np.where(tissue == EPITHELIUM, 2, 0)
        surf_mask = ndimage.filters.convolve(epi_mask, np.ones((3, 3, 3)))
        print('constructing surfactant layer and capillary...')
        # construct surfactant and blood vessel
        if not simple:
            tissue[np.logical_and(tissue == AIR, surf_mask > 0)] = SURF
        tissue[np.logical_and(tissue == REGULAR_TISSUE, surf_mask > 0)] = BLOOD

    def write_to_vtk(self, filename):
        zbin, ybin, xbin = self.shape

        f = open(filename, 'w')
        f.write('# vtk DataFile Version 4.2\n')
        f.write('Aspergillus simulation: Geometry\n')
        f.write('BINARY\n')
        f.write('DATASET STRUCTURED_POINTS\n')
        f.write('DIMENSIONS ' + str(xbin) + ' ' + str(ybin) + ' ' + str(zbin) + '\n')
        f.write('ASPECT_RATIO 1 1 1\n')
        f.write('ORIGIN 0 0 0\n')
        f.write('POINT_DATA ' + str(xbin * ybin * zbin) + '\n')
        f.write('SCALARS TissueType unsigned_char 1\n')
        f.write('LOOKUP_TABLE default\n')
        f.close()

        f = open(filename, 'ab')
        array = self.geo.flatten()
        array = array.astype(int)

        b = struct.pack(len(array) * 'B', *array)
        f.write(b)
        f.close()

    def write_to_hdf5(self, filename, laplacian):
        # Write data to HDF5
        with h5py.File(filename, 'w') as data_file:
            data_file.create_dataset('geometry', data=self.geo)

            if laplacian:
                # embed laplacian matrix for all layers
                # surfactant layer laplacian
                surf_lapl = discrete_laplacian(self.grid, self.geo == SURF)
                # epithelium layer laplacian
                epi_lapl = discrete_laplacian(self.grid, self.geo == EPITHELIUM)
                # capillary layer laplacian
                blood_lapl = discrete_laplacian(self.grid, self.geo == BLOOD)

                d = {'surf_lapl': surf_lapl, 'epi_lapl': epi_lapl, 'blood_lapl': blood_lapl}
                matrices = data_file.create_group('lapl_matrices')
                for name, lapl in d.items():
                    matrix = matrices.create_group(name)
                    matrix.create_dataset('data', data=lapl.data)
                    matrix.create_dataset('indptr', data=lapl.indptr)
                    matrix.create_dataset('indices', data=lapl.indices)
                    matrix.attrs['shape'] = lapl.shape

    def preview(self):
        zbin, ybin, xbin = self.shape
        data_importer = vtk.vtkImageImport()

        g = self.geo.flatten()
        g = np.uint8(g)
        data_string = g.tostring()
        data_importer.CopyImportVoidPointer(data_string, len(data_string))
        data_importer.SetDataScalarTypeToUnsignedChar()
        data_importer.SetNumberOfScalarComponents(1)

        data_importer.SetDataExtent(0, xbin - 1, 0, ybin - 1, 0, zbin - 1)
        data_importer.SetWholeExtent(0, xbin - 1, 0, ybin - 1, 0, zbin - 1)

        # Create transfer mapping scalar value to opacity
        opacity_transfer_function = vtk.vtkPiecewiseFunction()
        opacity_transfer_function.AddPoint(0, 0.0)
        opacity_transfer_function.AddPoint(1, 0.2)
        opacity_transfer_function.AddPoint(2, 0.005)
        opacity_transfer_function.AddPoint(3, 1)

        # Create transfer mapping scalar value to color
        color_transfer_function = vtk.vtkColorTransferFunction()
        color_transfer_function.AddRGBPoint(0, 0.0, 0.0, 1.0)
        color_transfer_function.AddRGBPoint(1, 1.0, 0.0, 0.0)
        color_transfer_function.AddRGBPoint(2, 0.0, 0.0, 1.0)
        color_transfer_function.AddRGBPoint(3, 1.0, 1.0, 1.0)

        # The property describes how the data will look
        volume_property = vtk.vtkVolumeProperty()
        volume_property.SetColor(color_transfer_function)
        volume_property.SetScalarOpacity(opacity_transfer_function)
        volume_property.SetInterpolationTypeToLinear()

        # The mapper / ray cast function know how to render the data
        volume_mapper = vtk.vtkGPUVolumeRayCastMapper()
        volume_mapper.SetBlendModeToComposite()
        volume_mapper.SetInputConnection(data_importer.GetOutputPort())

        # The volume holds the mapper and the property and
        # can be used to position/orient the volume
        volume = vtk.vtkVolume()
        volume.SetMapper(volume_mapper)
        volume.SetProperty(volume_property)

        ren = vtk.vtkRenderer()
        ren_win = vtk.vtkRenderWindow()
        ren_win.AddRenderer(ren)
        iren = vtk.vtkRenderWindowInteractor()
        iren.SetRenderWindow(ren_win)

        ren.AddVolume(volume)
        ren.SetBackground(1, 1, 1)
        ren_win.SetSize(600, 600)
        ren_win.Render()

        def check_abort(obj, event):
            if obj.GetEventPending() != 0:
                obj.SetAbortRender(1)

        ren_win.AddObserver('AbortCheckEvent', check_abort)

        iren.Initialize()
        ren_win.Render()
        iren.Start()

Methods

def add(self, function)

Add functions to the generator.

Expand source code
def add(self, function):
    """Add functions to the generator."""
    function.scale(self.scale)
    if function.type == SAC:
        self.sac_f.append(function)
    elif function.type == DUCT:
        self.duct_f.append(function)
    else:
        raise Exception('Unknown tissue type')
def construct(self, simple)

Construct the simulation space with math functions.

Expand source code
def construct(self, simple):
    """Construct the simulation space with math functions."""
    tissue = self.geo
    # fixed = self.fixed

    random_mask = np.random.normal(0, self.randomness, self.shape)

    self.construct_air_duct(random_mask)
    self.construct_alveolus(random_mask)

    epi_mask = np.where(tissue == EPITHELIUM, 2, 0)
    surf_mask = ndimage.filters.convolve(epi_mask, np.ones((3, 3, 3)))
    print('constructing surfactant layer and capillary...')
    # construct surfactant and blood vessel
    if not simple:
        tissue[np.logical_and(tissue == AIR, surf_mask > 0)] = SURF
    tissue[np.logical_and(tissue == REGULAR_TISSUE, surf_mask > 0)] = BLOOD
def construct_air_duct(self, random_mask)
Expand source code
def construct_air_duct(self, random_mask):
    print('constructing air duct...')
    tissue = self.geo
    fixed = self.fixed
    # construct air duct
    for function in self.duct_f:
        if isinstance(function, Cylinder):
            air_mask = self.construct_cylinder(
                tissue,
                function.center,
                function.length,
                function.direction,
                function.radius + random_mask,
            )
            tissue[np.logical_and(air_mask == 1, fixed == 0)] = AIR

    # blur the noise to maintain the continuousness of the air
    blur_mask = np.where(tissue == AIR, 1, 0)
    blur_air_mask = ndimage.filters.convolve(blur_mask, np.ones((3, 3, 3)))
    tissue[blur_air_mask > 13] = AIR
    tissue[blur_air_mask <= 13] = REGULAR_TISSUE
    fixed[tissue == AIR] = 1

    # construct epithelium layer
    air_mask = np.where(tissue == AIR, 1, 0)
    epithelium_mask = ndimage.filters.convolve(air_mask, np.ones((3, 3, 3)))
    tissue[np.logical_and(epithelium_mask > 0, tissue == REGULAR_TISSUE)] = EPITHELIUM
def construct_alveolus(self, random_mask)
Expand source code
def construct_alveolus(self, random_mask):
    tissue = self.geo
    fixed = self.fixed
    print('constructing alveolus...')
    # construct sac
    for function in self.sac_f:
        if isinstance(function, Sphere):
            air_mask = self.construct_sphere(
                tissue, function.center, function.radius + random_mask
            )
            blur_air_mask = ndimage.filters.convolve(air_mask, np.ones((3, 3, 3)))
            fixed_air_mask = np.logical_and(blur_air_mask > 13, fixed == 0)
            tissue[fixed_air_mask] = AIR
            tissue[
                np.logical_and(np.logical_and(blur_air_mask <= 13, fixed == 0), tissue == AIR)
            ] = REGULAR_TISSUE
            fixed[fixed_air_mask] = 1

            # construct epithelium layer
            epithelium_mask = ndimage.filters.convolve(fixed_air_mask, np.ones((3, 3, 3)))
            fixed_epithelium_mask = np.logical_and(epithelium_mask > 0, fixed == 0)
            tissue[fixed_epithelium_mask] = EPITHELIUM
            fixed[fixed_epithelium_mask] = 1
def construct_cylinder(self, lung_tissue: numpy.ndarray, center: Point, length: float, direction: numpy.ndarray, r: numpy.ndarray)

Construct cylinder within simulation space.

Expand source code
def construct_cylinder(
    self,
    lung_tissue: np.ndarray,
    center: Point,
    length: float,
    direction: np.ndarray,
    r: np.ndarray,
):
    """Construct cylinder within simulation space."""
    coords = np.indices(lung_tissue.shape, dtype=np.float64).T

    # normalize direction, just in case
    direction = direction / np.linalg.norm(direction)

    relative_coords: np.ndarray = coords - center
    distance_along_axis: np.ndarray = relative_coords @ direction
    distance_from_axis = np.linalg.norm(
        relative_coords - np.multiply.outer(relative_coords @ direction, direction), axis=3
    )
    mask = np.logical_and(distance_from_axis <= r.T, distance_along_axis <= (length / 2.0)).T
    return mask
def construct_sphere(self, lungtissue, center: Point, r: float)

Construct sphere within simulation space.

Expand source code
def construct_sphere(self, lungtissue, center: Point, r: float):
    """Construct sphere within simulation space."""
    coords = np.ogrid[: lungtissue.shape[0], : lungtissue.shape[1], : lungtissue.shape[2]]
    distance = np.sqrt(
        (coords[0] - center.z) ** 2 + (coords[1] - center.y) ** 2 + (coords[2] - center.x) ** 2
    )
    return 1 * (distance <= r)
def preview(self)
Expand source code
def preview(self):
    zbin, ybin, xbin = self.shape
    data_importer = vtk.vtkImageImport()

    g = self.geo.flatten()
    g = np.uint8(g)
    data_string = g.tostring()
    data_importer.CopyImportVoidPointer(data_string, len(data_string))
    data_importer.SetDataScalarTypeToUnsignedChar()
    data_importer.SetNumberOfScalarComponents(1)

    data_importer.SetDataExtent(0, xbin - 1, 0, ybin - 1, 0, zbin - 1)
    data_importer.SetWholeExtent(0, xbin - 1, 0, ybin - 1, 0, zbin - 1)

    # Create transfer mapping scalar value to opacity
    opacity_transfer_function = vtk.vtkPiecewiseFunction()
    opacity_transfer_function.AddPoint(0, 0.0)
    opacity_transfer_function.AddPoint(1, 0.2)
    opacity_transfer_function.AddPoint(2, 0.005)
    opacity_transfer_function.AddPoint(3, 1)

    # Create transfer mapping scalar value to color
    color_transfer_function = vtk.vtkColorTransferFunction()
    color_transfer_function.AddRGBPoint(0, 0.0, 0.0, 1.0)
    color_transfer_function.AddRGBPoint(1, 1.0, 0.0, 0.0)
    color_transfer_function.AddRGBPoint(2, 0.0, 0.0, 1.0)
    color_transfer_function.AddRGBPoint(3, 1.0, 1.0, 1.0)

    # The property describes how the data will look
    volume_property = vtk.vtkVolumeProperty()
    volume_property.SetColor(color_transfer_function)
    volume_property.SetScalarOpacity(opacity_transfer_function)
    volume_property.SetInterpolationTypeToLinear()

    # The mapper / ray cast function know how to render the data
    volume_mapper = vtk.vtkGPUVolumeRayCastMapper()
    volume_mapper.SetBlendModeToComposite()
    volume_mapper.SetInputConnection(data_importer.GetOutputPort())

    # The volume holds the mapper and the property and
    # can be used to position/orient the volume
    volume = vtk.vtkVolume()
    volume.SetMapper(volume_mapper)
    volume.SetProperty(volume_property)

    ren = vtk.vtkRenderer()
    ren_win = vtk.vtkRenderWindow()
    ren_win.AddRenderer(ren)
    iren = vtk.vtkRenderWindowInteractor()
    iren.SetRenderWindow(ren_win)

    ren.AddVolume(volume)
    ren.SetBackground(1, 1, 1)
    ren_win.SetSize(600, 600)
    ren_win.Render()

    def check_abort(obj, event):
        if obj.GetEventPending() != 0:
            obj.SetAbortRender(1)

    ren_win.AddObserver('AbortCheckEvent', check_abort)

    iren.Initialize()
    ren_win.Render()
    iren.Start()
def write_to_hdf5(self, filename, laplacian)
Expand source code
def write_to_hdf5(self, filename, laplacian):
    # Write data to HDF5
    with h5py.File(filename, 'w') as data_file:
        data_file.create_dataset('geometry', data=self.geo)

        if laplacian:
            # embed laplacian matrix for all layers
            # surfactant layer laplacian
            surf_lapl = discrete_laplacian(self.grid, self.geo == SURF)
            # epithelium layer laplacian
            epi_lapl = discrete_laplacian(self.grid, self.geo == EPITHELIUM)
            # capillary layer laplacian
            blood_lapl = discrete_laplacian(self.grid, self.geo == BLOOD)

            d = {'surf_lapl': surf_lapl, 'epi_lapl': epi_lapl, 'blood_lapl': blood_lapl}
            matrices = data_file.create_group('lapl_matrices')
            for name, lapl in d.items():
                matrix = matrices.create_group(name)
                matrix.create_dataset('data', data=lapl.data)
                matrix.create_dataset('indptr', data=lapl.indptr)
                matrix.create_dataset('indices', data=lapl.indices)
                matrix.attrs['shape'] = lapl.shape
def write_to_vtk(self, filename)
Expand source code
def write_to_vtk(self, filename):
    zbin, ybin, xbin = self.shape

    f = open(filename, 'w')
    f.write('# vtk DataFile Version 4.2\n')
    f.write('Aspergillus simulation: Geometry\n')
    f.write('BINARY\n')
    f.write('DATASET STRUCTURED_POINTS\n')
    f.write('DIMENSIONS ' + str(xbin) + ' ' + str(ybin) + ' ' + str(zbin) + '\n')
    f.write('ASPECT_RATIO 1 1 1\n')
    f.write('ORIGIN 0 0 0\n')
    f.write('POINT_DATA ' + str(xbin * ybin * zbin) + '\n')
    f.write('SCALARS TissueType unsigned_char 1\n')
    f.write('LOOKUP_TABLE default\n')
    f.close()

    f = open(filename, 'ab')
    array = self.geo.flatten()
    array = array.astype(int)

    b = struct.pack(len(array) * 'B', *array)
    f.write(b)
    f.close()