Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/multiassetpath #580

Open
wants to merge 12 commits into
base: alpha-dev
Choose a base branch
from
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ classifiers = [
]

[project.scripts]
helios = "helios.__main__:helios_entrypoint"
helios = "helios.__main__:cli"

[tool.scikit-build]
metadata.version.provider = "scikit_build_core.metadata.setuptools_scm"
Expand Down
4 changes: 2 additions & 2 deletions python/helios/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
del metadata

from helios.leg import Leg
from helios.platform import Platform, PlatformSettings
from helios.platforms import Platform, PlatformSettings
from helios.scanner import Scanner, ScannerSettings
from helios.scene import StaticScene, ScenePart
from helios.settings import (
Expand All @@ -17,4 +17,4 @@
set_output_settings,
)
from helios.survey import Survey
from helios.util import add_asset_directory, combine_parameters
from helios.utils import add_asset_directory, combine_parameters
343 changes: 318 additions & 25 deletions python/helios/__main__.py
Original file line number Diff line number Diff line change
@@ -1,38 +1,331 @@
import importlib_resources as resources
import os
import subprocess
import sys
import click
from click_option_group import optgroup, MutuallyExclusiveOptionGroup
from helios.settings import ExecutionSettings, OutputSettings, LogVerbosity
from helios.survey import Survey
from helios.utils import add_asset_directory, set_rng_seed


def _get_executable():
"""Locate the compiled Helios executable."""
return resources.files("_helios") / "helios" / "bin" / "helios++"
@click.command()
@click.argument("survey_file_path", required=True)
@optgroup.group("Input")
@optgroup.option(
"--assets",
"-a",
type=click.Path(exists=True, file_okay=False),
multiple=True,
help=(
"Specify the path(s) to assets/data directory. To specify multiple "
"paths, duplicate the argument,e.g. --assets path1 --assets path2. "
"By default: './assets/' and the Python package installation directory."
),
)
@optgroup.group("Output")
@optgroup.option(
"--output",
"-o",
type=click.Path(file_okay=False),
default="./output",
show_default=True,
help="Specify the path to output directory",
)
@optgroup.option(
"--splitByChannel",
is_flag=True,
help=(
"Use this flag to enable the one-file-per-device writing mode when "
"using a multi-channel scanner. "
"By default one-file-for-all writing is enabled "
),
)
@optgroup.option(
"--writeWaveform",
is_flag=True,
help=(
"Use this flag to enable full waveform writing By default waveform "
"is NOT written to output file"
),
)
@optgroup.option(
"--writePulse",
is_flag=True,
help=(
"Use this flag to enable pulse-wise data writing. By default "
"pulse-wise data is NOT written to output file"
),
)
@optgroup.option(
"--fullwaveNoise",
is_flag=True,
help=(
"Use this flag to add noise when computing full waveform. "
"By default: full waveform noise is disabled"
),
)
@optgroup.option(
"--format",
default="npy",
show_default=True,
help="Output format, can be las, laz, xyz, npy, or laspy.",
)
@optgroup.option(
"--lasOutput",
is_flag=True,
help="Use this flag to generate the output point cloud in LAS format (v 1.4)",
)
@optgroup.option(
"--las10", is_flag=True, help="Use this flag to write in LAS format (v 1.0)"
)
@optgroup.option(
"--lasScale",
type=click.FLOAT,
default=0.0001,
show_default=True,
help="Specify the decimal scale factor for LAS output",
)
@optgroup.option(
"--zipOutput", is_flag=True, help=("Use this flag to generate compressed output")
)
@optgroup.group("Execution")
@optgroup.option(
"--calcEchowidth",
is_flag=True,
help=(
"Use this flag to enable full waveform fitting. "
"By default the full waveform is NOT fitted"
),
)
@optgroup.option(
"--fixedIncidenceAngle",
is_flag=True,
help=(
"Use this flag to use fixed incidence angle. Fixed incidence angle of "
"exactly 0.0 will be considered for all intersection"
),
)
@optgroup.option(
"--seed",
type=click.INT,
help=(
"Specify the seed for randomness generation. It must be an intenger"
"By default: a random seed is generated."
),
)
@optgroup.option(
"--gpsStartTime",
type=click.STRING,
help=(
"Specify a fixed start time for GPS. It can be either a posix timestamp "
"or a 'YYYY-MM-DD hh:mm:ss+00:00' date time string, including "
"the timezone. By default: The current system time is used."
),
)
@optgroup.option(
"--parallelization",
type=click.INT,
default=0,
show_default=True,
help=(
"Specify the parallelization strategy. 0 for a static/dynamic chunk "
"based parallelization and 1 for a warehouse based one."
),
)
@optgroup.option(
"--num-threads",
"-j",
"--njobs",
type=click.INT,
show_default=True,
help=(
"Specify the number of threads to be used to compute the simulation. "
"By default: all available threads are used."
),
)
@optgroup.option(
"--chunkSize",
type=click.INT,
default=32,
show_default=True,
help=(
"Specify the chunk size to be used for parallel computing. If a "
"negative number is given, then its absolute value is used as starting "
"size of the dynamic chunk-size strategy. Positive numbers specify "
"the size for a static chunk-size strategy"
),
)
@optgroup.option(
"--warehouseFactor",
type=click.INT,
default=4,
show_default=True,
help=(
"Specify the warehouse factor. The number of tasks in the warehouse "
"would be k times the number of workers. The greater the factor, theless "
"the probability of idle cores but the greater the memory consumption."
),
)
@optgroup.option(
"--rebuildScene",
is_flag=True,
help=("Force scene rebuild even when a previously built scene is available"),
)
@optgroup.option(
"--noSceneWriting",
is_flag=True,
help=(
"If a scene is created during asset loading, it will be written by "
"default. Enabling this flag will prevent this writing."
),
)
@optgroup.option(
"--disablePlatformNoise",
is_flag=True,
help=(
"Disable platform noise, no matter what is specified on XML files. "
"By default: XML specifications are considered "
),
)
@optgroup.option(
"--disableLegNoise",
is_flag=True,
help=(
"Disable leg noise, no matter what is specified on XML files. "
"By default: XML specifications are considered "
),
)
@optgroup.group("KDTree")
@optgroup.option(
"--kdt",
type=click.INT,
default=4,
show_default=True,
help=(
"Specify the type of KDTree to be built for for the scene. Using 1 is "
"for the simple KDTree based on median balancing, 2 for the SAH "
"based KDTree, 3 for the SAH with best axis based KDTree and "
"4 (default) for a fast SAH approximation."
),
)
@optgroup.option(
"--kdtJobs",
type=click.INT,
help=(
"Specify the number of threads to be used for building the KDTree. "
"If 1, then the KDTree will be built in a sequential fashion. "
"If >1, then the KDTree will be built in a parallel fashion. By "
"default, the KDTree will be built using as many threads as available."
),
)
@optgroup.option(
"--kdtGeomJobs",
type=click.INT,
help=(
"Specify the number of threads to be used for bu upper nodes of the "
"KDTree (geometry-level parallelization). If 1, then there is no "
"geometry-level parallelization. If >1, then geometry-level "
"parallelization uses as many threads a . By default "
"geometry-level parallelization uses as many threads as node-level."
),
)
@optgroup.option(
"--sahNodes",
type=click.INT,
default=32,
show_default=True,
help=(
"Specify how many nodes must be used by the Surface Area Heuristic "
"when building a SAH based KDTree. For the SAH KDTree it is "
"recommended to be 21. More nodes lead to a best search process to "
"find split position, at the expenses of a greater computational "
"cost. When using a fast SAH approximation it is recommended to set "
"this to 32 (default). "
),
)
@optgroup.group("Logging", cls=MutuallyExclusiveOptionGroup)
@optgroup.option(
"--logFile",
is_flag=True,
help=(
"Logging will be outputted to a file, not only to standard output. "
"By default: logging will be written to standard output."
),
)
@optgroup.option(
"--logFileOnly",
is_flag=True,
help=(
"Logging will be outputted ONLY to a file. "
"By default: logging will be outputted to standard output."
),
)
@optgroup.group("Verbosity", cls=MutuallyExclusiveOptionGroup)
@optgroup.option(
"--verbose",
"-v",
count=True,
help=(
"Increase the verbosity level to include information. Can be repeated "
"once to report all messages. Default: report errors and warnings."
),
)
@optgroup.option(
"--silent",
"-s",
is_flag=True,
help=("Disable all logging output."),
)
@optgroup.option("--quiet", "-q", is_flag=True, help="Only errors are reported.")
@optgroup.option("--vt", is_flag=True, help="Report time and errors.")
@click.version_option()
def cli(**kw):

for asset in kw["assets"]:
add_asset_directory(asset)

def helios_exec(args):
#
# Inject additional arguments to account for standard paths
#
if seed := kw.get("seed") is not None:
set_rng_seed(seed)
else:
set_rng_seed()

# We always look for assets in the current working directory
args = args + ["--assets", os.getcwd()]
verbosity = LogVerbosity.DEFAULT
if kw.get("verbose") == 1:
verbosity = LogVerbosity.VERBOSE
elif kw.get("verbose") == 2:
verbosity = LogVerbosity.VERY_VERBOSE
elif kw.get("quiet"):
verbosity = LogVerbosity.QUIET
elif kw.get("vt"):
verbosity = LogVerbosity.TIME
elif kw.get("silent"):
verbosity = LogVerbosity.SILENT

# We always look in the Python installation tree
args = args + ["--assets", resources.files("helios")]
args = args + ["--assets", resources.files("helios") / "data"]
output_settings = OutputSettings()
execution_settings = ExecutionSettings()

# Inject the legacy model switch. This is part of our transitioning strategy
# to the new energy model.
args = args + ["--legacyEnergyModel"]
execution_settings.parallelization = kw.get("parallelization")
execution_settings.num_threads = kw.get("num_threads")
execution_settings.chunk_size = kw.get("chunksize")
execution_settings.warehouse_factor = kw.get("warehousefactor")
execution_settings.log_file = kw.get("logfile")
execution_settings.log_file_only = kw.get("logfileonly")
execution_settings.verbosity = verbosity
execution_settings.factory_type = kw.get("kdt")
execution_settings.kdt_num_threads = kw.get("kdtjobs")
execution_settings.kdt_geom_num_threads = kw.get("kdtgeomjobs")
execution_settings.sah_nodes = kw.get("sahnodes")

# Call the executable
executable = _get_executable()
return subprocess.call([executable] + args)
output_settings.format = kw.get("format")
output_settings.split_by_channel = kw.get("splitbychannel")
output_settings.output_dir = kw.get("output")
output_settings.write_waveform = kw.get("writewaveform")
output_settings.write_pulse = kw.get("writepulse")
output_settings.las_scale = kw.get("lasscale")

survey = Survey.from_xml(kw.get("survey_file_path"))
if gps := kw.get("gpsstarttime"):
survey.gps_time = gps

def helios_entrypoint():
raise SystemExit(helios_exec(sys.argv[1:]))
survey.run(execution_settings=execution_settings, output_settings=output_settings)


if __name__ == "__main__":
helios_entrypoint()
raise cli()
2 changes: 1 addition & 1 deletion python/helios/leg.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from helios.platform import PlatformSettingsBase, PlatformSettings
from helios.platforms import PlatformSettingsBase, PlatformSettings
from helios.scanner import ScannerSettings, ScannerSettingsBase
from helios.validation import Model

Expand Down
2 changes: 1 addition & 1 deletion python/helios/platform.py → python/helios/platforms.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from helios.util import get_asset_directories
from helios.utils import get_asset_directories
from helios.validation import (
AssetPath,
Model,
Expand Down
2 changes: 1 addition & 1 deletion python/helios/scanner.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from helios.util import get_asset_directories
from helios.utils import get_asset_directories
from helios.validation import (
AssetPath,
Model,
Expand Down
Loading