Skip to content
Closed

Vtk hdf #1084

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/cmake_macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,10 @@ jobs:
working-directory: ${{github.workspace}}/build
run: |
cmake $GITHUB_WORKSPACE -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DCMAKE_BUILD_TYPE=Debug \
-DENABLE_SAMRAI_TESTS=OFF -DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DlowResourceTests=ON \
-DCMAKE_CXX_FLAGS="-DPHARE_DIAG_DOUBLES=1 "
-DCMAKE_CXX_FLAGS="-O2 -DPHARE_DIAG_DOUBLES=1"

- name: Build
working-directory: ${{github.workspace}}/build
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/cmake_ubuntu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ jobs:
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DlowResourceTests=ON -DdevMode=ON -Dbench=ON \
-DCMAKE_CXX_FLAGS="-O3 -DPHARE_DIAG_DOUBLES=1 " -Dphare_configurator=ON
-DCMAKE_CXX_FLAGS="-O3 -DPHARE_DIAG_DOUBLES=1" -Dphare_configurator=ON

- name: Build
working-directory: ${{github.workspace}}/build
Expand Down
4 changes: 4 additions & 0 deletions pyphare/pyphare/core/box.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ def nCells(self):
"""returns the number of cells in the box"""
return self.shape.prod()

def size(self):
"""returns the number of cells in the box"""
return self.nCells()

def __str__(self):
return "Box({},{})".format(self.lower.tolist(), self.upper.tolist())

Expand Down
108 changes: 33 additions & 75 deletions pyphare/pyphare/core/operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,60 +2,47 @@

from pyphare.pharesee.hierarchy import ScalarField, VectorField
from pyphare.pharesee.hierarchy.hierarchy_utils import compute_hier_from
from pyphare.pharesee.hierarchy.hierarchy_utils import rename


def _compute_dot_product(patch_datas, **kwargs):
ref_name = next(iter(patch_datas.keys()))

def _compute_dot_product(patch0, hinfo, other, **kwargs):
patch1 = other.level(hinfo.ilvl, hinfo.time)[hinfo.patch_idx]
ref_name = next(iter(patch0.patch_datas.keys()))
dset = (
patch_datas["left_x"][:] * patch_datas["right_x"][:]
+ patch_datas["left_y"][:] * patch_datas["right_y"][:]
+ patch_datas["left_z"][:] * patch_datas["right_z"][:]
patch0["x"][:] * patch1["x"][:]
+ patch0["y"][:] * patch1["y"][:]
+ patch0["z"][:] * patch1["z"][:]
)

return (
{"name": "value", "data": dset, "centering": patch_datas[ref_name].centerings},
)
return ({"name": "value", "data": patch0[ref_name].copy_as(dset)},)


def _compute_sqrt(patch_datas, **kwargs):
ref_name = next(iter(patch_datas.keys()))
def _compute_sqrt(patch, **kwargs):
ref_name = next(iter(patch.patch_datas.keys()))

dset = np.sqrt(patch_datas["value"][:])
dset = np.sqrt(patch["value"][:])

return (
{"name": "value", "data": dset, "centering": patch_datas[ref_name].centerings},
)
return ({"name": "value", "data": patch[ref_name].copy_as(dset)},)


def _compute_cross_product(patch_datas, **kwargs):
ref_name = next(iter(patch_datas.keys()))
def _compute_cross_product(patch0, hinfo, other, **kwargs):
patch1 = other.level(hinfo.ilvl, hinfo.time)[hinfo.patch_idx]
ref_name = next(iter(patch0.patch_datas.keys()))

dset_x = (
patch_datas["left_y"][:] * patch_datas["right_z"][:]
- patch_datas["left_z"][:] * patch_datas["right_y"][:]
)
dset_y = (
patch_datas["left_z"][:] * patch_datas["right_x"][:]
- patch_datas["left_x"][:] * patch_datas["right_z"][:]
)
dset_z = (
patch_datas["left_x"][:] * patch_datas["right_y"][:]
- patch_datas["left_y"][:] * patch_datas["right_x"][:]
)
dset_x = patch0["y"][:] * patch1["z"][:] - patch0["z"][:] * patch1["y"][:]
dset_y = patch0["z"][:] * patch1["x"][:] - patch0["x"][:] * patch1["z"][:]
dset_z = patch0["x"][:] * patch1["y"][:] - patch0["y"][:] * patch1["x"][:]

return (
{"name": "x", "data": dset_x, "centering": patch_datas[ref_name].centerings},
{"name": "y", "data": dset_y, "centering": patch_datas[ref_name].centerings},
{"name": "z", "data": dset_z, "centering": patch_datas[ref_name].centerings},
{"name": "x", "data": patch0[ref_name].copy_as(dset_x)},
{"name": "y", "data": patch0[ref_name].copy_as(dset_y)},
{"name": "z", "data": patch0[ref_name].copy_as(dset_z)},
)


def _compute_grad(patch_data, **kwargs):
ndim = patch_data["value"].box.ndim
def _compute_grad(patch, **kwargs):
ndim = patch["value"].box.ndim
nb_ghosts = kwargs["nb_ghosts"]
ds = patch_data["value"].dataset
ds = patch["value"].dataset

ds_shape = list(ds.shape)

Expand All @@ -74,57 +61,28 @@ def _compute_grad(patch_data, **kwargs):
raise RuntimeError("dimension not yet implemented")

return (
{"name": "x", "data": ds_x, "centering": patch_data["value"].centerings},
{"name": "y", "data": ds_y, "centering": patch_data["value"].centerings},
{"name": "z", "data": ds_z, "centering": patch_data["value"].centerings},
{"name": "x", "data": patch["value"].copy_as(ds_x)},
{"name": "y", "data": patch["value"].copy_as(ds_y)},
{"name": "z", "data": patch["value"].copy_as(ds_z)},
)


def dot(hier_left, hier_right, **kwargs):
if isinstance(hier_left, VectorField) and isinstance(hier_right, VectorField):
names_left = ["left_x", "left_y", "left_z"]
names_right = ["right_x", "right_y", "right_z"]

else:
def dot(hier0, hier1, **kwargs):
if not all(isinstance(hier, VectorField) for hier in [hier0, hier1]):
raise RuntimeError("type of hierarchy not yet considered")

hl = rename(hier_left, names_left)
hr = rename(hier_right, names_right)
return ScalarField(compute_hier_from(_compute_dot_product, hier0, other=hier1))

h = compute_hier_from(
_compute_dot_product,
(hl, hr),
)

return ScalarField(h)


def cross(hier_left, hier_right, **kwargs):
if isinstance(hier_left, VectorField) and isinstance(hier_right, VectorField):
names_left = ["left_x", "left_y", "left_z"]
names_right = ["right_x", "right_y", "right_z"]

else:
def cross(hier0, hier1, **kwargs):
if not all(isinstance(hier, VectorField) for hier in [hier0, hier1]):
raise RuntimeError("type of hierarchy not yet considered")

hl = rename(hier_left, names_left)
hr = rename(hier_right, names_right)

h = compute_hier_from(
_compute_cross_product,
(hl, hr),
)

return VectorField(h)
return VectorField(compute_hier_from(_compute_cross_product, hier0, other=hier1))


def sqrt(hier, **kwargs):
h = compute_hier_from(
_compute_sqrt,
hier,
)

return ScalarField(h)
return ScalarField(compute_hier_from(_compute_sqrt, hier))


def modulus(hier):
Expand Down
12 changes: 12 additions & 0 deletions pyphare/pyphare/core/phare_utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,18 @@ def __ge__(self, other):
return fp_gtr_equal(self.fp, other.fp, self.atol)


class EqualityCheck:
def __init__(self, eq, msg=""):
self.eq = eq
self.msg = msg

def __bool__(self):
return self.eq

def __repr__(self):
return self.msg


def is_fp32(item):
if is_nd_array(item):
return item.dtype == np.single
Expand Down
3 changes: 3 additions & 0 deletions pyphare/pyphare/pharein/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,9 @@ def as_paths(rb):
)

if len(simulation.diagnostics) > 0:
if simulation.diag_options is not None and "format" in simulation.diag_options:
add_string(diag_path + "format", simulation.diag_options["format"])

if simulation.diag_options is not None and "options" in simulation.diag_options:
add_string(
diag_path + "filePath", simulation.diag_options["options"]["dir"]
Expand Down
6 changes: 3 additions & 3 deletions pyphare/pyphare/pharein/diagnostics.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,9 @@ def validate_timestamps(clazz, key, **kwargs):
timestamps = phare_utilities.np_array_ify(kwargs.get(key, []))

if np.any(timestamps < init_time):
raise RuntimeError(
f"Error: timestamp({sim.time_step_nbr}) cannot be less than simulation.init_time({init_time}))"
)
timestamps = timestamps[timestamps >= init_time]
print(f"Warning: some timestamps below ({init_time}) are filtered)")

Comment on lines 64 to +67
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix the warning message formatting.

Line 66 has an extra closing parenthesis, which reads oddly in user output.

🧹 Proposed fix
-        print(f"Warning: some timestamps below ({init_time}) are filtered)")
+        print(f"Warning: some timestamps below ({init_time}) are filtered")
🤖 Prompt for AI Agents
In `@pyphare/pyphare/pharein/diagnostics.py` around lines 64 - 67, The warning
print in the block that filters timestamps (the np.any(timestamps < init_time)
check) has an extra closing parenthesis; update the print statement (within that
timestamps >= init_time filtering branch) to a correctly formatted f-string
without the stray ')' — e.g. "print(f'Warning: some timestamps below
({init_time}) are filtered')" or a clearer variant like "print(f'Warning:
timestamps below {init_time} were filtered')" so the message is syntactically
correct and readable.

if np.any(timestamps > sim.final_time):
raise RuntimeError(
f"Error: timestamp({sim.time_step_nbr}) cannot be greater than simulation.final_time({sim.final_time}))"
Expand Down
40 changes: 23 additions & 17 deletions pyphare/pyphare/pharein/simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,31 +160,35 @@
and "time_step" not in kwargs
)

if not any([final_and_dt, final_and_nsteps, nsteps_and_dt]):
raise ValueError(
"Error: Specify either 'final_time' and 'time_step' or 'time_step_nbr' and 'time_step'"
+ " or 'final_time' and 'time_step_nbr'"
)

start_time = kwargs.get("restart_options", {}).get("restart_time", 0)

def _final_time():
if "final_time" in kwargs:
return kwargs["final_time"]
return start_time + kwargs["time_step"] * kwargs["time_step_nbr"]

final_time = _final_time()
total_time = final_time - start_time

if final_and_dt:
time_step_nbr = int(kwargs["final_time"] / kwargs["time_step"])
time_step = kwargs["final_time"] / time_step_nbr
time_step_nbr = int(total_time / kwargs["time_step"])
time_step = total_time / time_step_nbr

elif final_and_nsteps:
time_step = kwargs["final_time"] / kwargs["time_step_nbr"]
time_step = total_time / kwargs["time_step_nbr"]
time_step_nbr = kwargs["time_step_nbr"]

elif nsteps_and_dt:
time_step = kwargs["time_step"]
time_step_nbr = kwargs["time_step_nbr"]

else:
raise ValueError(
"Error: Specify either 'final_time' and 'time_step' or 'time_step_nbr' and 'time_step'"
+ " or 'final_time' and 'time_step_nbr'"
)

return (
time_step_nbr,
time_step,
kwargs.get("final_time", start_time + time_step * time_step_nbr),
)
return time_step_nbr, time_step, final_time

Check failure

Code scanning / CodeQL

Potentially uninitialized local variable Error

Local variable 'time_step' may be used before it is initialized.
Comment on lines +163 to +191
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Guard against zero/negative total_time causing division by zero

Line 180 can compute time_step_nbr = 0 when total_time <= 0 or total_time < time_step, which then divides by zero on Line 181; Line 184 also divides by time_step_nbr without validating it’s > 0. Add explicit validation before dividing.

🩹 Suggested fix
@@
     final_time = _final_time()
     total_time = final_time - start_time
+    if total_time <= 0:
+        raise ValueError("Error: final_time must be greater than restart_time")
+    if "time_step" in kwargs and kwargs["time_step"] <= 0:
+        raise ValueError("Error: time_step must be > 0")
@@
     if final_and_dt:
         time_step_nbr = int(total_time / kwargs["time_step"])
+        if time_step_nbr < 1:
+            raise ValueError(
+                "Error: final_time must be >= restart_time + time_step"
+            )
         time_step = total_time / time_step_nbr
@@
     elif final_and_nsteps:
+        if kwargs["time_step_nbr"] <= 0:
+            raise ValueError("Error: time_step_nbr must be > 0")
         time_step = total_time / kwargs["time_step_nbr"]
         time_step_nbr = kwargs["time_step_nbr"]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if not any([final_and_dt, final_and_nsteps, nsteps_and_dt]):
raise ValueError(
"Error: Specify either 'final_time' and 'time_step' or 'time_step_nbr' and 'time_step'"
+ " or 'final_time' and 'time_step_nbr'"
)
start_time = kwargs.get("restart_options", {}).get("restart_time", 0)
def _final_time():
if "final_time" in kwargs:
return kwargs["final_time"]
return start_time + kwargs["time_step"] * kwargs["time_step_nbr"]
final_time = _final_time()
total_time = final_time - start_time
if final_and_dt:
time_step_nbr = int(kwargs["final_time"] / kwargs["time_step"])
time_step = kwargs["final_time"] / time_step_nbr
time_step_nbr = int(total_time / kwargs["time_step"])
time_step = total_time / time_step_nbr
elif final_and_nsteps:
time_step = kwargs["final_time"] / kwargs["time_step_nbr"]
time_step = total_time / kwargs["time_step_nbr"]
time_step_nbr = kwargs["time_step_nbr"]
elif nsteps_and_dt:
time_step = kwargs["time_step"]
time_step_nbr = kwargs["time_step_nbr"]
else:
raise ValueError(
"Error: Specify either 'final_time' and 'time_step' or 'time_step_nbr' and 'time_step'"
+ " or 'final_time' and 'time_step_nbr'"
)
return (
time_step_nbr,
time_step,
kwargs.get("final_time", start_time + time_step * time_step_nbr),
)
return time_step_nbr, time_step, final_time
if not any([final_and_dt, final_and_nsteps, nsteps_and_dt]):
raise ValueError(
"Error: Specify either 'final_time' and 'time_step' or 'time_step_nbr' and 'time_step'"
" or 'final_time' and 'time_step_nbr'"
)
start_time = kwargs.get("restart_options", {}).get("restart_time", 0)
def _final_time():
if "final_time" in kwargs:
return kwargs["final_time"]
return start_time + kwargs["time_step"] * kwargs["time_step_nbr"]
final_time = _final_time()
total_time = final_time - start_time
if total_time <= 0:
raise ValueError("Error: final_time must be greater than restart_time")
if "time_step" in kwargs and kwargs["time_step"] <= 0:
raise ValueError("Error: time_step must be > 0")
if final_and_dt:
time_step_nbr = int(total_time / kwargs["time_step"])
if time_step_nbr < 1:
raise ValueError(
"Error: final_time must be >= restart_time + time_step"
)
time_step = total_time / time_step_nbr
elif final_and_nsteps:
if kwargs["time_step_nbr"] <= 0:
raise ValueError("Error: time_step_nbr must be > 0")
time_step = total_time / kwargs["time_step_nbr"]
time_step_nbr = kwargs["time_step_nbr"]
elif nsteps_and_dt:
time_step = kwargs["time_step"]
time_step_nbr = kwargs["time_step_nbr"]
return time_step_nbr, time_step, final_time
🤖 Prompt for AI Agents
In `@pyphare/pyphare/pharein/simulation.py` around lines 163 - 191, The code can
divide by zero when total_time <= 0 or when computed time_step_nbr becomes 0;
add explicit guards: compute start_time and total_time as before, then if
total_time <= 0 raise a ValueError indicating final_time must be > start_time;
when in the final_and_dt branch, after computing time_step_nbr = int(total_time
/ kwargs["time_step"]) verify time_step_nbr > 0 (raise ValueError if not) before
computing time_step, and similarly in the final_and_nsteps branch validate
kwargs["time_step_nbr"] > 0 before dividing to compute time_step; reference
variables/functions: _final_time, total_time, time_step_nbr, time_step,
final_and_dt, final_and_nsteps, nsteps_and_dt.



# ------------------------------------------------------------------------------
Expand Down Expand Up @@ -481,7 +485,7 @@
# diag_options = {"format":"phareh5", "options": {"dir": "phare_ouputs/"}}
def check_diag_options(**kwargs):
diag_options = kwargs.get("diag_options", None)
formats = ["phareh5"]
formats = ["phareh5", "pharevtkhdf"]
if diag_options is not None and "format" in diag_options:
if diag_options["format"] not in formats:
raise ValueError("Error - diag_options format is invalid")
Expand Down Expand Up @@ -727,8 +731,10 @@
kwargs["max_nbr_levels"],
) = check_refinement_boxes(ndim, **kwargs)
else:
kwargs["max_nbr_levels"] = kwargs.get("max_nbr_levels", None)
assert kwargs["max_nbr_levels"] is not None # this needs setting otherwise
if "max_nbr_levels" not in kwargs:
print("WARNING, 'max_nbr_levels' is not set, defaulting to 1")
kwargs["max_nbr_levels"] = kwargs.get("max_nbr_levels", 1)

kwargs["refinement_boxes"] = None
kwargs["tagging_threshold"] = kwargs.get("tagging_threshold", 0.1)

Expand Down
43 changes: 38 additions & 5 deletions pyphare/pyphare/pharesee/hierarchy/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
#
#
#

from .scalarfield import ScalarField
from .vectorfield import VectorField
from .hierarchy import PatchHierarchy
from pyphare.core.phare_utilities import listify


from pyphare.core import phare_utilities as phut

__all__ = [
"ScalarField",
Expand All @@ -11,11 +17,19 @@


def hierarchy_from(
simulator=None, qty=None, pop="", h5_filename=None, times=None, hier=None, func=None, **kwargs
simulator=None,
qty=None,
pop="",
h5_filename=None,
times=None,
hier=None,
func=None,
**kwargs,
):
from .fromh5 import hierarchy_fromh5
from .fromsim import hierarchy_from_sim
from .fromfunc import hierarchy_from_func
from .fromvtkhdf5 import hierarchy_fromvtkhdf

"""
this function reads an HDF5 PHARE file and returns a PatchHierarchy from
Expand All @@ -29,18 +43,37 @@ def hierarchy_from(
"""

if times is not None:
times = listify(times)
times = phut.listify(times)

if simulator is not None and h5_filename is not None:
raise ValueError("cannot pass both a simulator and a h5 file")

if h5_filename is not None:
return hierarchy_fromh5(h5_filename, times, hier, **kwargs)

if h5_filename.endswith(".h5"):
return hierarchy_fromh5(h5_filename, times, hier, **kwargs)
if h5_filename.endswith(".vtkhdf"):
return hierarchy_fromvtkhdf(h5_filename, times, hier, **kwargs)
raise RuntimeError(f"Unknown h5 file type: {h5_filename}")
if simulator is not None and qty is not None:
return hierarchy_from_sim(simulator, qty, pop=pop)

if func is not None and hier is not None:
return hierarchy_from_func(func, hier, **kwargs)

raise ValueError("can't make hierarchy")


def all_times_from(h5_filename):
if h5_filename.endswith(".h5"):
from .fromh5 import get_times_from_h5

return get_times_from_h5(h5_filename)
if h5_filename.endswith(".vtkhdf"):
from .fromvtkhdf5 import get_times_from_h5

return get_times_from_h5(h5_filename)
raise RuntimeError(f"Unknown h5 file type: {h5_filename}")


def default_time_from(h5_filename):
return all_times_from(h5_filename)[0]
2 changes: 2 additions & 0 deletions pyphare/pyphare/pharesee/hierarchy/for_vtk/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# do not make a python file or module called "vtk"
# this might interfer the actual vtk module
11 changes: 11 additions & 0 deletions pyphare/pyphare/pharesee/hierarchy/for_vtk/hierarchy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#
#
#


from pyphare.pharesee import hierarchy


class PatchHierarchy(hierarchy.PatchHierarchy):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
Loading
Loading