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
55 changes: 55 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ A Python library for interpolating physical field data (electromagnetic forces,
- Heat flux (scalar fields)
- Heat generation (volumetric)
- Heat Transfer Coefficient + bulk fluid temperature (convection BCs)
- **Analysis tools**:
- Scalar field integration for computing total heat generation, flux, etc.
- EM force resultant computation for validating force/moment conservation
- **Export to ANSYS APDL**: Direct export of interpolated results in APDL format
- **Visualization**: Built-in VTK export for ParaView or PyVista visualization
- **Efficient**: KDTree-based spatial queries for fast neighbor searches
Expand Down Expand Up @@ -60,6 +63,58 @@ interpolator.export_to_ansys("output_directory")
interpolator.build_vtk_output(outdir="vtk_output")
```

## Analysis Methods

After interpolation, InterpCore provides methods to analyze and validate results:

### Scalar Integrals

For scalar fields (heat flux, heat generation), you can compute the total integral over the destination mesh:

```python
# Requires volume or area data in the destination mesh
file_idx = {"ids": 0, "dest_x": 1, "src_x": 1, "val": 4, "vol": 4} # or "area": 5

interpolator = Interpolator(
path_to_src_folder="source_data",
path_to_dest_mesh="destination_mesh.txt",
config=config,
file_idx=file_idx
)

interpolator.interpolate_all()

# Compute integrals (e.g., total heat generation in W)
integrals = interpolator.compute_scalar_integrals()
# Returns: {"data_001": array([total_value])}
```

**Note**: The destination mesh must include volume (for 3D elements) or area (for 2D elements) data. Use the APDL scripts in [`apdl-scripts/`](apdl-scripts/) to export element centroids with volumes and areas.

### EM Force Resultants

For EM force fields, you can compute force and moment resultants to verify conservation:

```python
# After interpolation with EM_FORCE load type
resultants = interpolator.compute_EM_resultants(pole=np.array([0.0, 0.0, 0.0]))

# Returns for each source file:
# {
# "force_001": {
# "R_F_EM": [Fx, Fy, Fz], # Total force from source data
# "R_F_Mech": [Fx, Fy, Fz], # Total force from interpolated data
# "R_M_EM": [Mx, My, Mz], # Total moment from source data
# "R_M_Mech": [Mx, My, Mz], # Total moment from interpolated data
# "f_err_comp": [ex, ey, ez], # Relative force error by component
# "m_err_comp": [ex, ey, ez], # Relative moment error by component
# "Unmapped_EM_Force": float # Norm of unmapped forces
# }
# }
```

This is useful for validating that the interpolation preserves global force and moment equilibrium. Small errors indicate good interpolation quality.

## Examples

Complete working examples with sample data are available in the [`doc/`](doc/) folder:
Expand Down
19 changes: 15 additions & 4 deletions apdl-scripts/export_centroids.apdl
Original file line number Diff line number Diff line change
Expand Up @@ -26,29 +26,40 @@ CMSEL,S,%named_selection%
! Allocate arrays
*DIM, nGs, ARRAY, nE, 3 ! element centroid coordinates
*DIM, nTId,ARRAY, nE ! element ids
*DIM, nArea,ARRAY, nE ! element area
*DIM, nVol,ARRAY, nE ! element volume

! Loop through selected elements and store centroids
*DO,i,1,nE ! loop on selected elements
nTId(i) = iE ! element Id
nGs(i,1) = CENTRX(iE) ! centroid X
nGs(i,2) = CENTRY(iE) ! centroid Y
nGs(i,3) = CENTRZ(iE) ! centroid Z

! Get area and volume using *GET
*GET,area_val,ELEM,iE,AREA
*GET,vol_val,ELEM,iE,VOLU
nArea(i) = area_val ! element area
nVol(i) = vol_val ! element volume

iE = ELNEXT(iE)
*ENDDO

! Write results to CSV file
*CFOPEN,%output_file%,csv
! Write header
*VWRITE,'Element_ID','X','Y','Z'
(A,',',A,',',A,',',A)
*VWRITE,'Element_ID','X','Y','Z','Area','Volume'
(A,',',A,',',A,',',A,',',A,',',A)
! Write data
*VWRITE,nTId(1),nGs(1,1),nGs(1,2),nGs(1,3)
(F10.0,',',E16.9,',',E16.9,',',E16.9)
*VWRITE,nTId(1),nGs(1,1),nGs(1,2),nGs(1,3),nArea(1),nVol(1)
(F10.0,',',E16.9,',',E16.9,',',E16.9,',',E16.9,',',E16.9)
*CFCLOS

! Clean up arrays
*DIM, nGs, DELETE
*DIM, nTId, DELETE
*DIM, nArea, DELETE
*DIM, nVol, DELETE

*MSG,INFO
Read CDB file '%cdb_file%.cdb' and exported %nE% element centroids from '%named_selection%' to %output_file%.csv
Expand Down
80 changes: 73 additions & 7 deletions doc/em_force/em_force.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,19 @@
},
{
"cell_type": "code",
"execution_count": 21,
"execution_count": 1,
"id": "04f7aa2d",
"metadata": {},
"outputs": [],
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"c:\\ProgramData\\anaconda3\\envs\\interpcore\\Lib\\site-packages\\tqdm\\auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
" from .autonotebook import tqdm as notebook_tqdm\n"
]
}
],
"source": [
"from interpcore.interpolator import Interpolator\n",
"from interpcore.config import InterpolationConfig, QUERY_TYPE, INTERPOLATED_LOAD_TYPE\n",
Expand All @@ -32,15 +41,15 @@
},
{
"cell_type": "code",
"execution_count": 22,
"execution_count": 2,
"id": "42549f4f",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"100%|██████████| 1/1 [00:00<00:00, 113.32it/s]"
"100%|██████████| 1/1 [00:00<00:00, 81.83it/s]"
]
},
{
Expand Down Expand Up @@ -96,7 +105,7 @@
},
{
"cell_type": "code",
"execution_count": 23,
"execution_count": 3,
"id": "6dc76b31",
"metadata": {},
"outputs": [
Expand Down Expand Up @@ -146,7 +155,7 @@
},
{
"cell_type": "code",
"execution_count": 24,
"execution_count": 4,
"id": "d23ec832",
"metadata": {},
"outputs": [],
Expand All @@ -167,7 +176,7 @@
},
{
"cell_type": "code",
"execution_count": 25,
"execution_count": 5,
"id": "4adb006f",
"metadata": {},
"outputs": [
Expand Down Expand Up @@ -222,6 +231,63 @@
"plt.show()"
]
},
{
"cell_type": "markdown",
"id": "3b0d279d",
"metadata": {},
"source": [
"## Compute Force and Moment Resultants\n",
"\n",
"Verify that the interpolation preserves global force and moment equilibrium by computing resultants."
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "eb46e90a",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"============================================================\n",
"Results for: em_force_data\n",
"============================================================\n",
"\n",
"Force Resultant (Source): [797.1 373.7 136.35]\n",
"Force Resultant (Interpolated): [797.1 373.7 136.35]\n",
"Force Error (relative): [-2.85251130e-16 0.00000000e+00 4.16893428e-16]\n",
"\n",
"Moment Resultant (Source): [ 758.52 -181.42 -4007.15]\n",
"Moment Resultant (Interpolated): [ 756.21485572 -179.12590679 -3990.31095618]\n",
"Moment Error (relative): [0.003039 0.01264521 0.00420225]\n",
"\n",
"Unmapped EM Force (norm): 0.000000\n"
]
}
],
"source": [
"import numpy as np\n",
"\n",
"# Compute resultants with pole at origin\n",
"resultants = interpolator.compute_EM_resultants(pole=np.array([0.0, 0.0, 0.0]))\n",
"\n",
"# Display results for the first data file\n",
"for name, result in resultants.items():\n",
" print(f\"\\n{'='*60}\")\n",
" print(f\"Results for: {name}\")\n",
" print(f\"{'='*60}\")\n",
" print(f\"\\nForce Resultant (Source): {result['R_F_EM']}\")\n",
" print(f\"Force Resultant (Interpolated): {result['R_F_Mech']}\")\n",
" print(f\"Force Error (relative): {result['f_err_comp']}\")\n",
" print(f\"\\nMoment Resultant (Source): {result['R_M_EM']}\")\n",
" print(f\"Moment Resultant (Interpolated): {result['R_M_Mech']}\")\n",
" print(f\"Moment Error (relative): {result['m_err_comp']}\")\n",
" print(f\"\\nUnmapped EM Force (norm): {result['Unmapped_EM_Force']:.6f}\")"
]
},
{
"cell_type": "code",
"execution_count": null,
Expand Down
Loading
Loading