Skip to content

Commit f5c2128

Browse files
Porous Jump Python Interface (#1332)
* added porous jump bc to python client as well as tests * run black on code * fixed comments and ran isort + black * updated test * formatting * added tests for input validation for porous jump entity pairs * removed unnecessary code * test_validators_params.py * formatting * removed test --------- Co-authored-by: Ben <106089368+benflexcompute@users.noreply.github.com>
1 parent 04074e7 commit f5c2128

File tree

8 files changed

+416
-0
lines changed

8 files changed

+416
-0
lines changed

flow360/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
MassFlowRate,
6161
Outflow,
6262
Periodic,
63+
PorousJump,
6364
Pressure,
6465
Rotational,
6566
SlaterPorousBleed,
@@ -206,6 +207,7 @@
206207
"Outflow",
207208
"Inflow",
208209
"Periodic",
210+
"PorousJump",
209211
"SymmetryPlane",
210212
"Fluid",
211213
"Solid",

flow360/component/simulation/models/surface_models.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
AbsoluteTemperatureType,
3333
AngularVelocityType,
3434
HeatFluxType,
35+
InverseAreaType,
36+
InverseLengthType,
3537
LengthType,
3638
MassFlowRateType,
3739
PressureType,
@@ -693,6 +695,60 @@ def ensure_surface_existence(cls, value):
693695
return value
694696

695697

698+
class PorousJump(Flow360BaseModel):
699+
"""
700+
:class:`PorousJump` defines the Porous Jump boundary condition.
701+
702+
Example
703+
-------
704+
705+
- Define a porous jump condition:
706+
707+
>>> fl.PorousJump(
708+
... surface_pairs=[
709+
... (volume_mesh["blk-1/Interface-blk-2"], volume_mesh["blk-2/Interface-blk-1"]),
710+
... (volume_mesh["blk-1/Interface-blk-3"], volume_mesh["blk-3/Interface-blk-1"]),
711+
... ],
712+
... darcy_coefficient = 1e6 / fl.u.m **2,
713+
... forchheimer_coefficient = 1 / fl.u.m,
714+
... thickness = 1 * fl.u.m,
715+
... )
716+
====
717+
"""
718+
719+
name: Optional[str] = pd.Field(
720+
"PorousJump", description="Name of the `PorousJump` boundary condition."
721+
)
722+
type: Literal["PorousJump"] = pd.Field("PorousJump", frozen=True)
723+
entity_pairs: UniqueItemList[SurfacePair] = pd.Field(
724+
alias="surface_pairs", description="List of matching pairs of :class:`~flow360.Surface`. "
725+
)
726+
darcy_coefficient: InverseAreaType = pd.Field(
727+
description="Darcy coefficient of the porous media model which determines the scaling of the "
728+
+ "viscous loss term. The value defines the coefficient for the axis normal "
729+
+ "to the surface."
730+
)
731+
forchheimer_coefficient: InverseLengthType = pd.Field(
732+
description="Forchheimer coefficient of the porous media model which determines "
733+
+ "the scaling of the inertial loss term."
734+
)
735+
thickness: LengthType = pd.Field(
736+
description="Thickness of the thin porous media on the surface"
737+
)
738+
739+
@pd.field_validator("entity_pairs", mode="after")
740+
@classmethod
741+
def ensure_surface_existence(cls, value):
742+
"""Ensure all boundaries will be present after mesher and all entities are surfaces"""
743+
for surface_pair in value.items:
744+
check_deleted_surface_pair(surface_pair)
745+
for surface in surface_pair.pair:
746+
if not surface.private_attribute_is_interface:
747+
raise ValueError(f"Boundary `{surface.name}` is not an interface")
748+
749+
return value
750+
751+
696752
SurfaceModelTypes = Union[
697753
Wall,
698754
SlipWall,
@@ -701,4 +757,5 @@ def ensure_surface_existence(cls, value):
701757
Inflow,
702758
Periodic,
703759
SymmetryPlane,
760+
PorousJump,
704761
]

flow360/component/simulation/translator/solver_translator.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
MassFlowRate,
2121
Outflow,
2222
Periodic,
23+
PorousJump,
2324
Pressure,
2425
SlaterPorousBleed,
2526
SlipWall,
@@ -1245,6 +1246,11 @@ def boundary_spec_translator(model: SurfaceModelTypes, op_acoustic_to_static_pre
12451246
boundary = _append_turbulence_quantities_to_dict(model, model_dict, boundary)
12461247
elif isinstance(model, SymmetryPlane):
12471248
boundary["type"] = "SymmetryPlane"
1249+
elif isinstance(model, PorousJump):
1250+
boundary["type"] = "PorousJump"
1251+
boundary["DarcyCoefficient"] = model_dict["darcyCoefficient"]
1252+
boundary["ForchheimerCoefficient"] = model_dict["forchheimerCoefficient"]
1253+
boundary["porousJumpThickness"] = model_dict["thickness"]
12481254

12491255
return boundary
12501256

@@ -1616,6 +1622,7 @@ def get_solver_json(
16161622
Inflow,
16171623
Periodic,
16181624
SymmetryPlane,
1625+
PorousJump,
16191626
),
16201627
boundary_spec_translator,
16211628
to_list=False,

flow360/component/simulation/validation/validation_simulation_params.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from flow360.component.simulation.models.surface_models import (
99
Inflow,
1010
Outflow,
11+
PorousJump,
1112
SurfaceModelTypes,
1213
Wall,
1314
)
@@ -362,6 +363,8 @@ def _check_complete_boundary_condition_and_unknown_surface(
362363
for model in params.models:
363364
if not isinstance(model, get_args(SurfaceModelTypes)):
364365
continue
366+
if isinstance(model, PorousJump):
367+
continue
365368

366369
entities = []
367370
# pylint: disable=protected-access

tests/simulation/params/test_validators_params.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
Inflow,
3434
Outflow,
3535
Periodic,
36+
PorousJump,
3637
Pressure,
3738
SlaterPorousBleed,
3839
SlipWall,
@@ -932,6 +933,35 @@ def test_incomplete_BC_surface_mesh():
932933
)
933934

934935

936+
def test_porousJump_entities_is_interface():
937+
surface_1_is_interface = Surface(name="Surface-1", private_attribute_is_interface=True)
938+
surface_2_is_not_interface = Surface(name="Surface-2", private_attribute_is_interface=False)
939+
surface_3_is_interface = Surface(name="Surface-3", private_attribute_is_interface=True)
940+
error_message = "Boundary `Surface-2` is not an interface"
941+
with pytest.raises(ValueError, match=re.escape(error_message)):
942+
porousJump = PorousJump(
943+
entity_pairs=[(surface_1_is_interface, surface_2_is_not_interface)],
944+
darcy_coefficient=1e6 / (u.m * u.m),
945+
forchheimer_coefficient=1e3 / u.m,
946+
thickness=0.01 * u.m,
947+
)
948+
949+
with pytest.raises(ValueError, match=re.escape(error_message)):
950+
porousJump = PorousJump(
951+
entity_pairs=[(surface_2_is_not_interface, surface_1_is_interface)],
952+
darcy_coefficient=1e6,
953+
forchheimer_coefficient=1e3,
954+
thickness=0.01,
955+
)
956+
957+
porousJump = PorousJump(
958+
entity_pairs=[(surface_1_is_interface, surface_3_is_interface)],
959+
darcy_coefficient=1e6 / (u.m * u.m),
960+
forchheimer_coefficient=1e3 / u.m,
961+
thickness=0.01 * u.m,
962+
)
963+
964+
935965
def test_duplicate_entities_in_models():
936966
entity_generic_volume = GenericVolume(name="Duplicate Volume")
937967
entity_surface = Surface(name="Duplicate Surface")
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
{
2+
"boundaries": {
3+
"blk-1/Interface_blk-2": {
4+
"DarcyCoefficient": 1e6,
5+
"ForchheimerCoefficient": 10.0,
6+
"pairedPatchName": "blk-2/Interface_blk-1",
7+
"porousJumpThickness": 0.1,
8+
"type": "PorousJump"
9+
},
10+
"blk-1/outflow": {
11+
"staticPressureRatio": 1.0,
12+
"type": "SubsonicOutflowPressure"
13+
},
14+
"blk-1/slip": {
15+
"type": "SlipWall"
16+
},
17+
"blk-2/Interface_blk-1": {
18+
"DarcyCoefficient": 1e6,
19+
"ForchheimerCoefficient": 10.0,
20+
"porousJumpThickness": 0.1,
21+
"type": "PorousJump"
22+
},
23+
"blk-2/slip": {
24+
"type": "SlipWall"
25+
},
26+
"blk-3/inflow": {
27+
"totalPressureRatio": 1.028281,
28+
"totalTemperatureRatio": 1.008,
29+
"type": "SubsonicInflow"
30+
},
31+
"blk-3/slip": {
32+
"type": "SlipWall"
33+
}
34+
},
35+
"freestream": {
36+
"Mach": 0.19999999999999998,
37+
"Temperature": -1,
38+
"alphaAngle": 0,
39+
"betaAngle": 0,
40+
"muRef": 2.0000000000000003e-06
41+
},
42+
"geometry": {},
43+
"initialCondition": {
44+
"p": "p",
45+
"rho": "rho",
46+
"type": "initialCondition",
47+
"u": "u",
48+
"v": "v",
49+
"w": "w"
50+
},
51+
"navierStokesSolver": {
52+
"CFLMultiplier": 1.0,
53+
"absoluteTolerance": 1e-10,
54+
"equationEvalFrequency": 1,
55+
"kappaMUSCL": 0.01,
56+
"limitPressureDensity": false,
57+
"limitVelocity": false,
58+
"linearSolver": {
59+
"maxIterations": 25
60+
},
61+
"lowMachPreconditioner": false,
62+
"maxForceJacUpdatePhysicalSteps": 0,
63+
"modelType": "Compressible",
64+
"numericalDissipationFactor": 1.0,
65+
"orderOfAccuracy": 2,
66+
"relativeTolerance": 0.0,
67+
"updateJacobianFrequency": 4
68+
},
69+
"outputRescale": {
70+
"velocityScale": 1.0
71+
},
72+
"surfaceOutput": {
73+
"animationFrequency": -1,
74+
"animationFrequencyOffset": 0,
75+
"animationFrequencyTimeAverage": -1,
76+
"animationFrequencyTimeAverageOffset": 0,
77+
"computeTimeAverages": false,
78+
"outputFields": [],
79+
"outputFormat": "paraview",
80+
"startAverageIntegrationStep": -1,
81+
"surfaces": {
82+
"blk-1/outflow": {
83+
"outputFields": [
84+
"Cf",
85+
"CfVec",
86+
"Cp",
87+
"Mach",
88+
"primitiveVars",
89+
"wallDistance",
90+
"yPlus"
91+
]
92+
},
93+
"blk-1/slip": {
94+
"outputFields": [
95+
"Cf",
96+
"CfVec",
97+
"Cp",
98+
"Mach",
99+
"primitiveVars",
100+
"wallDistance",
101+
"yPlus"
102+
]
103+
},
104+
"blk-2/slip": {
105+
"outputFields": [
106+
"Cf",
107+
"CfVec",
108+
"Cp",
109+
"Mach",
110+
"primitiveVars",
111+
"wallDistance",
112+
"yPlus"
113+
]
114+
},
115+
"blk-3/inflow": {
116+
"outputFields": [
117+
"Cf",
118+
"CfVec",
119+
"Cp",
120+
"Mach",
121+
"primitiveVars",
122+
"wallDistance",
123+
"yPlus"
124+
]
125+
},
126+
"blk-3/slip": {
127+
"outputFields": [
128+
"Cf",
129+
"CfVec",
130+
"Cp",
131+
"Mach",
132+
"primitiveVars",
133+
"wallDistance",
134+
"yPlus"
135+
]
136+
}
137+
},
138+
"writeSingleFile": false
139+
},
140+
"timeStepping": {
141+
"CFL": {
142+
"final": 100.0,
143+
"initial": 1.0,
144+
"rampSteps": 100,
145+
"type": "ramp"
146+
},
147+
"maxPseudoSteps": 2000,
148+
"orderOfAccuracy": 2,
149+
"physicalSteps": 1,
150+
"timeStepSize": "inf"
151+
},
152+
"turbulenceModelSolver": {
153+
"modelType": "None"
154+
},
155+
"userDefinedFields": [],
156+
"usingLiquidAsMaterial": false,
157+
"volumeOutput": {
158+
"animationFrequency": -1,
159+
"animationFrequencyOffset": 0,
160+
"animationFrequencyTimeAverage": -1,
161+
"animationFrequencyTimeAverageOffset": 0,
162+
"computeTimeAverages": false,
163+
"outputFields": [
164+
"Cp",
165+
"T",
166+
"mut",
167+
"mutRatio",
168+
"primitiveVars",
169+
"residualNavierStokes",
170+
"s",
171+
"vorticity",
172+
"vorticityMagnitude"
173+
],
174+
"outputFormat": "paraview",
175+
"startAverageIntegrationStep": -1
176+
}
177+
}

tests/simulation/translator/test_solver_translator.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@
8787
from tests.simulation.translator.utils.plateASI_param_generator import (
8888
create_plateASI_param,
8989
)
90+
from tests.simulation.translator.utils.porousJump_params_generator import (
91+
create_porous_jump_zone_param,
92+
)
9093
from tests.simulation.translator.utils.porousMedia_param_generator import (
9194
create_porous_media_box_param,
9295
create_porous_media_volume_zone_param,
@@ -235,6 +238,15 @@ def test_om6wing_tutorial(get_om6Wing_tutorial_param):
235238
)
236239

237240

241+
def test_porous_jump(create_porous_jump_zone_param):
242+
translate_and_compare(
243+
create_porous_jump_zone_param,
244+
mesh_unit=1 * u.m,
245+
ref_json_file="Flow360_porous_jump.json",
246+
debug=True,
247+
)
248+
249+
238250
def test_om6wing_temperature(get_om6Wing_tutorial_param):
239251
params = get_om6Wing_tutorial_param
240252
params.operating_condition.thermal_state = ThermalState(temperature=15 * u.degC)

0 commit comments

Comments
 (0)