Skip to content
Merged
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
7 changes: 1 addition & 6 deletions .github/workflows/build_and_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,8 @@ jobs:
# ────────────────────────────────────────────────────────────────
# Pytest checks
# ────────────────────────────────────────────────────────────────
- name: Install Pixi Python deps
run: |
pixi --version
pixi install

- name: Run pytest checks on HDF5 output
run: pixi run pytest
run: pixi run -e dashboard pytest

- uses: actions/upload-artifact@v4
if: failure()
Expand Down
7 changes: 1 addition & 6 deletions .github/workflows/build_and_test_mac.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,8 @@ jobs:
# ────────────────────────────────────────────────────────────────
# Pytest checks
# ────────────────────────────────────────────────────────────────
- name: Install Pixi Python deps
run: |
pixi --version
pixi install

- name: Run pytest checks on HDF5 output
run: pixi run pytest
run: pixi run -e dashboard pytest

- uses: actions/upload-artifact@v4
if: failure()
Expand Down
10 changes: 7 additions & 3 deletions .github/workflows/pixi_build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,10 @@ jobs:
build:
name: ${{ matrix.os }}
runs-on: ${{ matrix.os }}
env:
FANS_BUILD_DIR: build
strategy:
fail-fast: false
matrix:
os: [macos-latest, ubuntu-latest]
os: [ubuntu-latest]
steps:
- name: Checkout code
uses: actions/checkout@v5
Expand All @@ -32,6 +30,12 @@ jobs:
- name: Run pytest checks on HDF5 output
run: pixi run test

- name: Upload test logs
uses: actions/upload-artifact@v4
with:
name: test-logs
path: test/*.log

- uses: actions/upload-artifact@v4
if: failure()
with:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -210,3 +210,4 @@ test/input_files/**/*.json
# pixi environments
.pixi
*.egg-info
*.conda
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## latest

- Introduce pixi build workflow and conftest.py for pytest-fixture [#82](https://github.com/DataAnalyticsEngineering/FANS/pull/82)
- Use dashboard environment for regular pytest [#81](https://github.com/DataAnalyticsEngineering/FANS/pull/81)
- Fix bug in J2 plasticity routine [#80](https://github.com/DataAnalyticsEngineering/FANS/pull/80)

## v0.4.2
Expand Down
1,035 changes: 1,035 additions & 0 deletions pixi.lock

Large diffs are not rendered by default.

44 changes: 41 additions & 3 deletions pixi.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
channels = ["conda-forge"]
name = "FANS"
platforms = ["linux-64", "osx-64", "osx-arm64"]
preview = ["pixi-build"]

[dependencies]
fans = { path = "." }

[feature.dashboard.dependencies]
python = ">=3.13.5,<3.14"
Expand All @@ -23,10 +27,44 @@ precommit = "pre-commit run --all-files"
h52xdmf = { args = ["file"], cmd = "cd \"$INIT_CWD\" && python -m fans_dashboard.plotting.h52xdmf -t -v {{file}}" }

[tasks]
test = {depends-on = [
{ task = "pytest", environment = "dashboard" }, # Will be changed later with pixi-build
]}
test = { cmd = "pytest -v -s --from-pixi", cwd = "test/pytest", depends-on = ["test-fans"] }
test-fans = { cmd = "./run_tests.sh -n {{n}}", args = [
{ arg = "n", default = "2" }, # Number of threads
], cwd = "test" }

[environments]
default = {features = ["dashboard"]}
dashboard = { features = ["dashboard"], no-default-feature = true }

################## Pixi build part

[package]
name = "fans"
version = "0.4.2"

[package.build]
backend = { name = "pixi-build-cmake", version = "0.3.*" }

[package.build.config]
extra-args = [
"-DFANS_LIBRARY_FOR_MICRO_MANAGER=ON",
]

[workspace.target.osx-arm64.build-variants]
cxx_compiler_version = ["18"]

[workspace.target.linux-64.build-variants]
cxx_compiler = ["clangxx"]
cxx_compiler_version = ["18"]

[package.build-dependencies]
cmake = "==4.0.3"
pybind11 = "*"
pkg-config = "*"
ninja = "1.13.*"

[package.host-dependencies]
hdf5 = { version = "*", build = "* mpi_openmpi*" }
fftw = { version = "*", build = "* mpi_openmpi*" } # works
openmpi-mpicxx = "*"
eigen = "*"
2 changes: 1 addition & 1 deletion test/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ This will run all test cases and generate result files in `build/test/`.
After running the tests, the results are verified using pytest. We recommend running pytest via a pre-configured Pixi task,

```bash
pixi run pytest
pixi run -e dashboard pytest
```

Note: The validation tests expect result files to be in `build/test/` directory, so make sure to run the tests first.
Expand Down
43 changes: 43 additions & 0 deletions test/pytest/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import pytest
import os


def pytest_addoption(parser):
parser.addoption(
"--from-pixi",
action="store_true",
default=False,
help="Run tests from pixi task.",
)


@pytest.fixture(
params=[
"test_J2Plasticity",
"test_LinearElastic",
"test_LinearThermal",
"test_PseudoPlastic",
]
)
def test_files(request):
json_base_dir = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "../input_files/"
)
# Determine the HDF5 base directory based on the --from-pixi option
# if run from pixi, use output directory; otherwise, use build/test directory, as it is the one used by ctest
if request.config.getoption("--from-pixi"):
h5_base_dir = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "../output"
)
else:
h5_base_dir = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "../../build/test/"
)

json_path = os.path.join(json_base_dir, f"{request.param}.json")
h5_path = os.path.join(h5_base_dir, f"{request.param}.h5")

if os.path.exists(json_path) and os.path.exists(h5_path):
return json_path, h5_path

assert False, f"Required test files not found: {json_path} or {h5_path}"
24 changes: 0 additions & 24 deletions test/pytest/test_displacement_averaging.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,6 @@
from fans_dashboard.core.utils import identify_hierarchy, extract_and_organize_data


@pytest.fixture(
params=[
"test_J2Plasticity",
"test_LinearElastic",
"test_LinearThermal",
"test_PseudoPlastic",
]
)
def test_files(request):
json_base_dir = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "../input_files/"
)
h5_base_dir = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "../../build/test/"
)

json_path = os.path.join(json_base_dir, f"{request.param}.json")
h5_path = os.path.join(h5_base_dir, f"{request.param}.h5")

if os.path.exists(json_path) and os.path.exists(h5_path):
return json_path, h5_path
pytest.skip(f"Required test files not found: {json_path} or {h5_path}")


def test_displacement_averaging(test_files):
"""
This test verifies that the average of displacement fluctuations is zero for all
Expand Down
24 changes: 0 additions & 24 deletions test/pytest/test_homogenization_consistency.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,6 @@
from fans_dashboard.core.utils import identify_hierarchy, extract_and_organize_data


@pytest.fixture(
params=[
"test_J2Plasticity",
"test_LinearElastic",
"test_LinearThermal",
"test_PseudoPlastic",
]
)
def test_files(request):
json_base_dir = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "../input_files/"
)
h5_base_dir = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "../../build/test/"
)

json_path = os.path.join(json_base_dir, f"{request.param}.json")
h5_path = os.path.join(h5_base_dir, f"{request.param}.h5")

if os.path.exists(json_path) and os.path.exists(h5_path):
return json_path, h5_path
pytest.skip(f"Required test files not found: {json_path} or {h5_path}")


def test_homogenization_consistency(test_files):
"""
This test verifies that the relationship stress_average = homogenized_tangent * strain_average
Expand Down
24 changes: 0 additions & 24 deletions test/pytest/test_homogenized_tangent_spd.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,6 @@
from scipy.linalg import eigvalsh


@pytest.fixture(
params=[
"test_J2Plasticity",
"test_LinearElastic",
"test_LinearThermal",
"test_PseudoPlastic",
]
)
def test_files(request):
json_base_dir = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "../input_files/"
)
h5_base_dir = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "../../build/test/"
)

json_path = os.path.join(json_base_dir, f"{request.param}.json")
h5_path = os.path.join(h5_base_dir, f"{request.param}.h5")

if os.path.exists(json_path) and os.path.exists(h5_path):
return json_path, h5_path
pytest.skip(f"Required test files not found: {json_path} or {h5_path}")


def test_homogenized_tangent_spd(test_files):
"""
This test verifies that the homogenized tangent is strictly Symmetric Positive Definite (SPD)
Expand Down
24 changes: 0 additions & 24 deletions test/pytest/test_homogenized_tangent_within_VRbounds.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,30 +11,6 @@
)


@pytest.fixture(
params=[
"test_J2Plasticity",
"test_LinearElastic",
"test_LinearThermal",
"test_PseudoPlastic",
]
)
def test_files(request):
json_base_dir = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "../input_files/"
)
h5_base_dir = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "../../build/test/"
)

json_path = os.path.join(json_base_dir, f"{request.param}.json")
h5_path = os.path.join(h5_base_dir, f"{request.param}.h5")

if os.path.exists(json_path) and os.path.exists(h5_path):
return json_path, h5_path
pytest.skip(f"Required test files not found: {json_path} or {h5_path}")


def test_homogenized_tangent_within_VRbounds(test_files):
"""
This test verifies that the homogenized tangent is within Voigt and Reuss bounds
Expand Down
24 changes: 0 additions & 24 deletions test/pytest/test_loading_to_strain_average.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,6 @@
from fans_dashboard.core.utils import identify_hierarchy, extract_and_organize_data


@pytest.fixture(
params=[
"test_J2Plasticity",
"test_LinearElastic",
"test_LinearThermal",
"test_PseudoPlastic",
]
)
def test_files(request):
json_base_dir = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "../input_files/"
)
h5_base_dir = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "../../build/test/"
)

json_path = os.path.join(json_base_dir, f"{request.param}.json")
h5_path = os.path.join(h5_base_dir, f"{request.param}.h5")

if os.path.exists(json_path) and os.path.exists(h5_path):
return json_path, h5_path
pytest.skip(f"Required test files not found: {json_path} or {h5_path}")


def test_loading_to_strain_average(test_files):
"""
This test verifies that the strain_average field in the results matches the macroscale_loading
Expand Down
24 changes: 0 additions & 24 deletions test/pytest/test_strain_stress_averaging.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,6 @@
from fans_dashboard.core.utils import identify_hierarchy, extract_and_organize_data


@pytest.fixture(
params=[
"test_J2Plasticity",
"test_LinearElastic",
"test_LinearThermal",
"test_PseudoPlastic",
]
)
def test_files(request):
json_base_dir = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "../input_files/"
)
h5_base_dir = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "../../build/test/"
)

json_path = os.path.join(json_base_dir, f"{request.param}.json")
h5_path = os.path.join(h5_base_dir, f"{request.param}.h5")

if os.path.exists(json_path) and os.path.exists(h5_path):
return json_path, h5_path
pytest.skip(f"Required test files not found: {json_path} or {h5_path}")


def test_strain_stress_averaging(test_files):
"""
This test verifies that the average of strain/stress fields matches the strain_average/stress_average
Expand Down
19 changes: 14 additions & 5 deletions test/run_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,25 @@ fi

num_processes=$2

# Select executable depending on whether we're inside a conda/pixi env
if [ -n "$CONDA_PREFIX" ]; then
FANS_EXEC="$CONDA_PREFIX/bin/FANS"
else
FANS_EXEC="./FANS"
fi

TIME_CMD="command time -v"
[[ "$OSTYPE" == "darwin"* ]] && TIME_CMD="command gtime -v"

mkdir -p output

# Run the jobs serially
$TIME_CMD mpiexec -n $num_processes ./FANS input_files/test_LinearThermal.json test_LinearThermal.h5 > test_LinearThermal.log 2>&1
$TIME_CMD mpiexec -n $num_processes "$FANS_EXEC" input_files/test_LinearThermal.json output/test_LinearThermal.h5 > test_LinearThermal.log 2>&1

$TIME_CMD mpiexec -n $num_processes ./FANS input_files/test_LinearElastic.json test_LinearElastic.h5 > test_LinearElastic.log 2>&1
$TIME_CMD mpiexec -n $num_processes "$FANS_EXEC" input_files/test_LinearElastic.json output/test_LinearElastic.h5 > test_LinearElastic.log 2>&1

$TIME_CMD mpiexec -n $num_processes ./FANS input_files/test_PseudoPlastic.json test_PseudoPlastic.h5 > test_PseudoPlastic.log 2>&1
$TIME_CMD mpiexec -n $num_processes "$FANS_EXEC" input_files/test_PseudoPlastic.json output/test_PseudoPlastic.h5 > test_PseudoPlastic.log 2>&1

$TIME_CMD mpiexec -n $num_processes ./FANS input_files/test_J2Plasticity.json test_J2Plasticity.h5 > test_J2Plasticity.log 2>&1
$TIME_CMD mpiexec -n $num_processes "$FANS_EXEC" input_files/test_J2Plasticity.json output/test_J2Plasticity.h5 > test_J2Plasticity.log 2>&1

$TIME_CMD mpiexec -n $num_processes ./FANS input_files/test_MixedBCs.json test_MixedBCs.h5 > test_MixedBCs.log 2>&1
$TIME_CMD mpiexec -n $num_processes "$FANS_EXEC" input_files/test_MixedBCs.json output/test_MixedBCs.h5 > test_MixedBCs.log 2>&1
Loading