Skip to content
Open
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
135 changes: 135 additions & 0 deletions .github/workflows/build-wheels.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
name: Build Patched Netgen Wheels

# NETGEN_VERSION: update this when syncing upstream (match official netgen-mesher PyPI version)
env:
NETGEN_VERSION: "6.2.2602.post1"

on:
workflow_dispatch:
push:
tags: ['v*']

jobs:
build:
name: Build wheel (Python ${{ matrix.python-version }})
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
python-version: ['3.10', '3.11', '3.12']

steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: add-setgeominfo-api

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Install build dependencies
run: |
python -m pip install --upgrade pip
pip install scikit-build wheel numpy pybind11-stubgen>=2.5
pip install netgen-occt-devel==7.8.1 netgen-occt==7.8.1
pip install packaging requests

- name: Build wheel
run: python setup.py bdist_wheel
env:
NETGEN_ARCH: avx2
NETGEN_VERSION_PYTHON: ${{ env.NETGEN_VERSION }}

- name: Install and test
run: |
pip install numpy netgen-occt==7.8.1
pip install (Get-ChildItem -Path dist/*.whl).FullName --force-reinstall
python -c "
import netgen
from netgen.meshing import Mesh as NGMesh, Element2D, FaceDescriptor, MeshPoint, Pnt
m = NGMesh()
m.Add(FaceDescriptor(surfnr=1, domin=0, domout=0, bc=1))
pnums = [m.Add(MeshPoint(Pnt(0,0,0))),
m.Add(MeshPoint(Pnt(1,0,0))),
m.Add(MeshPoint(Pnt(0,1,0)))]
el = Element2D(1, pnums)
el.SetGeomInfo(0, 0.0, 0.0)
el.SetGeomInfo(1, 1.0, 0.0)
el.SetGeomInfo(2, 0.0, 1.0)
print('SetGeomInfo API: OK')
"
shell: pwsh

- name: Upload wheel artifact
uses: actions/upload-artifact@v4
with:
name: wheel-${{ matrix.python-version }}
path: dist/*.whl

release:
name: Create GitHub Release
needs: build
runs-on: ubuntu-latest
permissions:
contents: write

steps:
- name: Download all wheel artifacts
uses: actions/download-artifact@v4
with:
path: dist
merge-multiple: true

- name: Generate install instructions
id: install_body
run: |
VER="${{ env.NETGEN_VERSION }}"
TAG="v${VER}-setgeominfo"
BASE_URL="https://github.com/ksugahar/netgen/releases/download/${TAG}"
WHEELS=$(ls dist/*.whl | xargs -I{} basename {} | sed "s|^|${BASE_URL}/|")
WHEEL_LINES=""
for w in $WHEELS; do
PY=$(basename $w | grep -oP 'cp\d+' | head -1 | sed 's/cp//')
PY_VER="${PY:0:1}.${PY:1}"
WHEEL_LINES="${WHEEL_LINES}- Python ${PY_VER}: \`pip install ${w} --force-reinstall\`\n"
done
echo "wheel_lines<<EOF" >> $GITHUB_OUTPUT
echo -e "$WHEEL_LINES" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT

- name: Create Release
uses: softprops/action-gh-release@v2
with:
tag_name: v${{ env.NETGEN_VERSION }}-setgeominfo
name: "Netgen ${{ env.NETGEN_VERSION }} + SetGeomInfo API"
body: |
Patched `netgen-mesher` wheels with [SetGeomInfo API](https://github.com/NGSolve/netgen/pull/232).

Same version as official `netgen-mesher==${{ env.NETGEN_VERSION }}` — satisfies ngsolve's version requirement.

## Install

```bash
# 1. Install official ngsolve (installs official netgen-mesher)
pip install ngsolve

# 2. Replace netgen-mesher with patched version (direct URL, no source build)
# Choose your Python version:
```

${{ steps.install_body.outputs.wheel_lines }}

> **Note**: Do NOT use `--find-links` with GitHub releases URL — it triggers a source build that fails.
> Always use the direct wheel URL above.

## What is SetGeomInfo?

`Element2D.SetGeomInfo(vertex_index, u, v)` sets UV parametric coordinates
on surface mesh elements, enabling `mesh.Curve(order)` for externally imported
meshes (Coreform Cubit, GMSH, etc.).

PR: https://github.com/NGSolve/netgen/pull/232
files: dist/*.whl
make_latest: true
72 changes: 72 additions & 0 deletions .github/workflows/sync-upstream.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
name: Sync Upstream Netgen

on:
schedule:
- cron: '0 9 * * 1' # Every Monday 9:00 UTC
workflow_dispatch:
inputs:
upstream_tag:
description: 'Upstream tag to sync to (e.g. v6.2.2602). Leave empty for latest.'
required: false

jobs:
sync:
runs-on: ubuntu-latest
permissions:
contents: write

steps:
- name: Checkout fork
uses: actions/checkout@v4
with:
ref: add-setgeominfo-api
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}

- name: Add upstream remote
run: git remote add upstream https://github.com/NGSolve/netgen.git

- name: Fetch upstream
run: git fetch upstream --tags

- name: Determine target tag
id: target
run: |
if [ -n "${{ github.event.inputs.upstream_tag }}" ]; then
TAG="${{ github.event.inputs.upstream_tag }}"
else
TAG=$(git tag -l 'v6.*' --sort=-version:refname | head -1)
fi
echo "tag=$TAG" >> $GITHUB_OUTPUT
echo "Target upstream tag: $TAG"

- name: Rebase SetGeomInfo patch onto upstream
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"

# Find the SetGeomInfo commits (2 commits on top of upstream)
PATCH_COMMITS=$(git log --oneline --format='%H' upstream/master..HEAD | tac)
echo "Patch commits to rebase:"
echo "$PATCH_COMMITS"

# Rebase onto the target tag
git rebase ${{ steps.target.outputs.tag }} || {
echo "::warning::Rebase conflict detected. Manual intervention required."
git rebase --abort
exit 1
}

echo "Rebase successful onto ${{ steps.target.outputs.tag }}"

- name: Push rebased branch
run: git push --force-with-lease origin add-setgeominfo-api

- name: Create summary
run: |
echo "## Upstream Sync Complete" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- **Target tag**: ${{ steps.target.outputs.tag }}" >> $GITHUB_STEP_SUMMARY
echo "- **Branch**: add-setgeominfo-api" >> $GITHUB_STEP_SUMMARY
echo "- **Patch commits**:" >> $GITHUB_STEP_SUMMARY
git log --oneline -3 >> $GITHUB_STEP_SUMMARY
13 changes: 13 additions & 0 deletions libsrc/core/autodiff.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1006,6 +1006,19 @@ NETGEN_INLINE AutoDiffVec<D,SCAL> asin (AutoDiffVec<D,SCAL> x)
return AutoDiffRec<D,SCAL> (cosh(x.Rec()), sinh(x.Value())*x.Last());
}

template <int D, typename SCAL>
auto asinh (AutoDiffRec<D,SCAL> x)
{
return AutoDiffRec<D,SCAL> (asinh(x.Rec()), 1/sqrt(sqr(x.Value()+1))*x.Last());
}

template <int D, typename SCAL>
auto acosh (AutoDiffRec<D,SCAL> x)
{
return AutoDiffRec<D,SCAL> (acosh(x.Rec()), 1/sqrt(sqr(x.Value()-1))*x.Last());
}


template <int D, typename SCAL>
auto erf (AutoDiffRec<D,SCAL> x)
{
Expand Down
2 changes: 1 addition & 1 deletion libsrc/core/hashtable.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ namespace ngcore
template <size_t J>
constexpr T get() const { return i[J]; }

operator FlatArray<T> () { return FlatArray<T> (N, &i[0]); }
operator FlatArray<T> () { return FlatArray<T> (N, i.Ptr()); }

NETGEN_INLINE IVec<N,T> & operator= (T value)
{
Expand Down
14 changes: 14 additions & 0 deletions libsrc/core/simd_generic.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -932,6 +932,20 @@ namespace ngcore
return ngcore::SIMD<double,N>([a](int i)->double { return cosh(a[i]); } );
}

using std::asinh;
template <int N>
NETGEN_INLINE ngcore::SIMD<double,N> asinh (ngcore::SIMD<double,N> a) {
return ngcore::SIMD<double,N>([a](int i)->double { return asinh(a[i]); } );
}

using std::acosh;
template <int N>
NETGEN_INLINE ngcore::SIMD<double,N> acosh (ngcore::SIMD<double,N> a) {
return ngcore::SIMD<double,N>([a](int i)->double { return acosh(a[i]); } );
}



template<int N, typename T>
using MultiSIMD = SIMD<T, N*GetDefaultSIMDSize()>;

Expand Down
4 changes: 4 additions & 0 deletions libsrc/core/taskmanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,10 @@ namespace ngcore

if(sleep)
std::this_thread::sleep_for(std::chrono::microseconds(sleep_usecs));
else if(no_job_counter > 30000)
std::this_thread::sleep_for(std::chrono::microseconds(1000));
else if(no_job_counter > 20000)
std::this_thread::sleep_for(std::chrono::microseconds(100));
else if(no_job_counter > 10000)
std::this_thread::sleep_for(std::chrono::microseconds(10));
else
Expand Down
1 change: 1 addition & 0 deletions libsrc/interface/writeabaqus.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ static inline const AbaqusElementType & GetAbaqusType(int dim, int num_nodes)
// 2D
AbaqusElementTypes{
{3, AbaqusElementType{"CPS3", vector{0,1,2}}},
{6, AbaqusElementType{"CPS6", vector{0,1,2,5,6,4}}},
},
// 3D
AbaqusElementTypes{
Expand Down
4 changes: 2 additions & 2 deletions libsrc/meshing/curvedelems.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4110,7 +4110,7 @@ namespace netgen
T lami[4];
TFlatVector<T> vlami(4, lami);

NgArrayMem<Point<2,T>, 50> coarse_xi (npts);
ArrayMem<Point<2,T>, 50> coarse_xi (npts);

for (int pi = 0; pi < npts; pi++)
{
Expand All @@ -4128,7 +4128,7 @@ namespace netgen

mesh.coarsemesh->GetCurvedElements().
CalcMultiPointSurfaceTransformation<DIM_SPACE,T> (hpref_el.coarse_elnr, npts,
&coarse_xi[0](0), &coarse_xi[1](0)-&coarse_xi[0](0),
&coarse_xi[0](0), sizeof(Point<2,T>)/sizeof(T),
x, sx, dxdxi, sdxdxi);

// Mat<3,2> dxdxic;
Expand Down
22 changes: 21 additions & 1 deletion libsrc/meshing/meshclass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7583,6 +7583,17 @@ namespace netgen
}
}

FlatArray<string*> Mesh :: GetRegionNamesCD (int codim) const
{
switch (codim)
{
case 0: return materials;
case 1: return bcnames;
case 2: return cd2names;
case 3: return cd3names;
default: throw Exception("don't have regions of co-dimension "+ToString(codim));
}
}

std::string_view Mesh :: GetRegionName (const Segment & el) const
{
Expand All @@ -7591,7 +7602,16 @@ namespace netgen

std::string_view Mesh :: GetRegionName (const Element2d & el) const
{
return *const_cast<Mesh&>(*this).GetRegionNamesCD(GetDimension()-2)[GetFaceDescriptor(el).BCProperty()-1];
// return *const_cast<Mesh&>(*this).GetRegionNamesCD(GetDimension()-2)[GetFaceDescriptor(el).BCProperty()-1];

auto ind = GetFaceDescriptor(el).BCProperty()-1;
auto names = this->GetRegionNamesCD(GetDimension()-2);

if (!names.Range().Contains(ind))
return defaultstring;
if (!names[ind])
return defaultstring;
return *names[ind];
}

std::string_view Mesh :: GetRegionName (const Element & el) const
Expand Down
1 change: 1 addition & 0 deletions libsrc/meshing/meshclass.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -779,6 +779,7 @@ namespace netgen


DLL_HEADER Array<string*> & GetRegionNamesCD (int codim);
DLL_HEADER FlatArray<string*> GetRegionNamesCD (int codim) const;

DLL_HEADER std::string_view GetRegionName(const Segment & el) const;
DLL_HEADER std::string_view GetRegionName(const Element2d & el) const;
Expand Down
24 changes: 24 additions & 0 deletions libsrc/meshing/python_mesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,21 @@ DLL_HEADER void ExportNetgenMeshing(py::module &m)
li.append(py::make_tuple(pgi.trignum, pgi.u, pgi.v));
return li;
})
.def("SetGeomInfo", [](Element2d& self, int vertex_index, double u, double v, int trignum)
{
int np = self.GetNP();
if (vertex_index < 0 || vertex_index >= np)
throw NgException("vertex_index out of range [0, " + std::to_string(np-1) + "]");
auto& gi = self.GeomInfoPi(vertex_index + 1);
gi.u = u;
gi.v = v;
gi.trignum = trignum;
}, py::arg("vertex_index"), py::arg("u"), py::arg("v"), py::arg("trignum")=0,
"Set geometry info (UV parameters) for a vertex of this surface element. "
"This is useful for external meshes where geominfo is not automatically set. "
"Parameters: vertex_index (0-based index), u/v (surface parametric coordinates), "
"trignum (triangle number for STL meshing, default: 0)"
)
.def_property_readonly("vertices",
FunctionPointer([](const Element2d & self) -> py::list
{
Expand Down Expand Up @@ -1557,6 +1572,15 @@ py::arg("point_tolerance") = -1.)
{
self.SetNextTimeStamp();
})
.def ("CalcSurfacesOfNode", &Mesh::CalcSurfacesOfNode,
"Rebuild surface element lookup tables (surface-on-node, "
"surface element hash table, boundary edges). "
"Call this after manually adding surface elements to ensure "
"consistent mesh topology for HDivSurface and BEM computations."
)
.def ("RebuildSurfaceElementLists", &Mesh::RebuildSurfaceElementLists,
"Rebuild internal linked lists of surface elements per FaceDescriptor."
)
.def ("CalcTotalBadness", &Mesh::CalcTotalBad)
.def ("GetQualityHistogram", &Mesh::GetQualityHistogram)
.def("Mirror", &Mesh::Mirror)
Expand Down
19 changes: 19 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[build-system]
requires = [
"scikit-build",
"wheel",
"numpy",
"pybind11-stubgen>=2.5",
"netgen-occt-devel==7.8.1",
"netgen-occt==7.8.1",
"packaging",
"requests",
]

[project]
name = "netgen-mesher"
dynamic = ["version"]
description = "Netgen Mesh Generator"
license = {text = "LGPL-2.1-only"}
requires-python = ">=3.10"
dependencies = ["numpy"]
Loading