- mass_fluxThe postprocessor name holding the mass flux at this point in kg/s (positive is flux in, negative is flux out)C++ Type:PostprocessorName Unit:(no unit assumed) Controllable:No Description:The postprocessor name holding the mass flux at this point in kg/s (positive is flux in, negative is flux out) 
- pointThe x,y,z coordinates of the point source (or sink)C++ Type:libMesh::Point Controllable:No Description:The x,y,z coordinates of the point source (or sink) 
- variableThe name of the variable that this residual object operates onC++ Type:NonlinearVariableName Unit:(no unit assumed) Controllable:No Description:The name of the variable that this residual object operates on 
PorousFlowPointSourceFromPostprocessor
PorousFlowPointSourceFromPostprocessor implements a mass point source that adds (or removes) fluid at a mass flux rate that was computed by a postprocessor.
For instance:
[DiracKernels<<<{"href": "../../syntax/DiracKernels/index.html"}>>>]
  [source]
    type = PorousFlowPointSourceFromPostprocessor<<<{"description": "Point source (or sink) that adds (or removes) fluid at a mass flux rate specified by a postprocessor.", "href": "PorousFlowPointSourceFromPostprocessor.html"}>>>
    variable<<<{"description": "The name of the variable that this residual object operates on"}>>> = pp
    mass_flux<<<{"description": "The postprocessor name holding the mass flux at this point in kg/s (positive is flux in, negative is flux out)"}>>> = mass_flux_in
    point<<<{"description": "The x,y,z coordinates of the point source (or sink)"}>>> = '0.5 0.5 0'
  []
[]Note that the execute_on parameter is set to timestep_begin so that the correct value is being used within the timestep.
[Postprocessors<<<{"href": "../../syntax/Postprocessors/index.html"}>>>]
  [total_mass]
    type = PorousFlowFluidMass<<<{"description": "Calculates the mass of a fluid component in a region", "href": "../postprocessors/PorousFlowFluidMass.html"}>>>
    execute_on<<<{"description": "The list of flag(s) indicating when this object should be executed. For a description of each flag, see https://mooseframework.inl.gov/source/interfaces/SetupInterface.html."}>>> = 'initial timestep_end'
  []
  [mass_flux_in]
    type = FunctionValuePostprocessor<<<{"description": "Computes the value of a supplied function at a single point (scalable)", "href": "../postprocessors/FunctionValuePostprocessor.html"}>>>
    function<<<{"description": "The function which supplies the postprocessor value."}>>> = mass_flux_fn
    execute_on<<<{"description": "The list of flag(s) indicating when this object should be executed. For a description of each flag, see https://mooseframework.inl.gov/source/interfaces/SetupInterface.html."}>>> = 'initial timestep_begin'
  []
[]Input Parameters
- allow_moving_sourcesFalseIf true, allow Dirac sources to move, even if the mesh does not move, during the simulation.Default:False C++ Type:bool Controllable:No Description:If true, allow Dirac sources to move, even if the mesh does not move, during the simulation. 
- blockThe list of blocks (ids or names) that this object will be appliedC++ Type:std::vector<SubdomainName> Controllable:No Description:The list of blocks (ids or names) that this object will be applied 
- matrix_onlyFalseWhether this object is only doing assembly to matrices (no vectors)Default:False C++ Type:bool Controllable:No Description:Whether this object is only doing assembly to matrices (no vectors) 
- point_not_found_behaviorIGNOREBy default (IGNORE), it is ignored if an added point cannot be located in the specified subdomains. If this option is set to ERROR, this situation will result in an error. If this option is set to WARNING, then a warning will be issued.Default:IGNORE C++ Type:MooseEnum Options:ERROR, WARNING, IGNORE Controllable:No Description:By default (IGNORE), it is ignored if an added point cannot be located in the specified subdomains. If this option is set to ERROR, this situation will result in an error. If this option is set to WARNING, then a warning will be issued. 
Optional Parameters
- absolute_value_vector_tagsThe tags for the vectors this residual object should fill with the absolute value of the residual contributionC++ Type:std::vector<TagName> Controllable:No Description:The tags for the vectors this residual object should fill with the absolute value of the residual contribution 
- extra_matrix_tagsThe extra tags for the matrices this Kernel should fillC++ Type:std::vector<TagName> Controllable:No Description:The extra tags for the matrices this Kernel should fill 
- extra_vector_tagsThe extra tags for the vectors this Kernel should fillC++ Type:std::vector<TagName> Controllable:No Description:The extra tags for the vectors this Kernel should fill 
- matrix_tagssystemThe tag for the matrices this Kernel should fillDefault:system C++ Type:MultiMooseEnum Options:nontime, system Controllable:No Description:The tag for the matrices this Kernel should fill 
- vector_tagsnontimeThe tag for the vectors this Kernel should fillDefault:nontime C++ Type:MultiMooseEnum Options:nontime, time Controllable:No Description:The tag for the vectors this Kernel should fill 
Contribution To Tagged Field Data Parameters
- control_tagsAdds user-defined labels for accessing object parameters via control logic.C++ Type:std::vector<std::string> Controllable:No Description:Adds user-defined labels for accessing object parameters via control logic. 
- drop_duplicate_pointsTrueBy default points added to a DiracKernel are dropped if a point at the same locationhas been added before. If this option is set to false duplicate points are retainedand contribute to residual and Jacobian.Default:True C++ Type:bool Controllable:No Description:By default points added to a DiracKernel are dropped if a point at the same locationhas been added before. If this option is set to false duplicate points are retainedand contribute to residual and Jacobian. 
- enableTrueSet the enabled status of the MooseObject.Default:True C++ Type:bool Controllable:Yes Description:Set the enabled status of the MooseObject. 
- implicitTrueDetermines whether this object is calculated using an implicit or explicit formDefault:True C++ Type:bool Controllable:No Description:Determines whether this object is calculated using an implicit or explicit form 
- search_methodnearest_node_connected_sidesChoice of search algorithm. All options begin by finding the nearest node in the primary boundary to a query point in the secondary boundary. In the default nearest_node_connected_sides algorithm, primary boundary elements are searched iff that nearest node is one of their nodes. This is fast to determine via a pregenerated node-to-elem map and is robust on conforming meshes. In the optional all_proximate_sides algorithm, primary boundary elements are searched iff they touch that nearest node, even if they are not topologically connected to it. This is more CPU-intensive but is necessary for robustness on any boundary surfaces which has disconnections (such as Flex IGA meshes) or non-conformity (such as hanging nodes in adaptively h-refined meshes).Default:nearest_node_connected_sides C++ Type:MooseEnum Options:nearest_node_connected_sides, all_proximate_sides Controllable:No Description:Choice of search algorithm. All options begin by finding the nearest node in the primary boundary to a query point in the secondary boundary. In the default nearest_node_connected_sides algorithm, primary boundary elements are searched iff that nearest node is one of their nodes. This is fast to determine via a pregenerated node-to-elem map and is robust on conforming meshes. In the optional all_proximate_sides algorithm, primary boundary elements are searched iff they touch that nearest node, even if they are not topologically connected to it. This is more CPU-intensive but is necessary for robustness on any boundary surfaces which has disconnections (such as Flex IGA meshes) or non-conformity (such as hanging nodes in adaptively h-refined meshes). 
- seed0The seed for the master random number generatorDefault:0 C++ Type:unsigned int Controllable:No Description:The seed for the master random number generator 
- use_displaced_meshFalseWhether or not this object should use the displaced mesh for computation. Note that in the case this is true but no displacements are provided in the Mesh block the undisplaced mesh will still be used.Default:False C++ Type:bool Controllable:No Description:Whether or not this object should use the displaced mesh for computation. Note that in the case this is true but no displacements are provided in the Mesh block the undisplaced mesh will still be used. 
Advanced Parameters
- prop_getter_suffixAn optional suffix parameter that can be appended to any attempt to retrieve/get material properties. The suffix will be prepended with a '_' character.C++ Type:MaterialPropertyName Unit:(no unit assumed) Controllable:No Description:An optional suffix parameter that can be appended to any attempt to retrieve/get material properties. The suffix will be prepended with a '_' character. 
- use_interpolated_stateFalseFor the old and older state use projected material properties interpolated at the quadrature points. To set up projection use the ProjectedStatefulMaterialStorageAction.Default:False C++ Type:bool Controllable:No Description:For the old and older state use projected material properties interpolated at the quadrature points. To set up projection use the ProjectedStatefulMaterialStorageAction. 
Material Property Retrieval Parameters
Input Files
- (modules/combined/examples/geochem-porous_flow/forge/porous_flow.i)
- (modules/porous_flow/test/tests/hysteresis/2phasePS.i)
- (modules/porous_flow/examples/multiapp_fracture_flow/3dFracture/fracture_only_aperture_changing.i)
- (modules/porous_flow/test/tests/hysteresis/hys_order_09.i)
- (modules/porous_flow/test/tests/hysteresis/hys_order_06.i)
- (modules/porous_flow/test/tests/hysteresis/2phasePS_relperm.i)
- (modules/porous_flow/test/tests/hysteresis/1phase_3rd.i)
- (modules/porous_flow/test/tests/dirackernels/frompps.i)
- (modules/porous_flow/test/tests/hysteresis/2phasePS_relperm_2.i)
- (modules/porous_flow/test/tests/hysteresis/hys_order_03.i)
- (modules/porous_flow/test/tests/hysteresis/hys_order_04.i)
- (modules/porous_flow/test/tests/sinks/s14.i)
- (modules/porous_flow/test/tests/hysteresis/2phasePS_2.i)
- (modules/porous_flow/test/tests/energy_conservation/heat04_rz.i)
- (modules/porous_flow/test/tests/hysteresis/hys_order_07.i)
- (modules/porous_flow/test/tests/mass_conservation/mass11.i)
- (modules/porous_flow/test/tests/hysteresis/hys_order_01.i)
- (modules/porous_flow/test/tests/sinks/s15.i)
- (modules/porous_flow/test/tests/hysteresis/hys_order_02.i)
- (modules/porous_flow/test/tests/dirackernels/hfrompps.i)
- (modules/porous_flow/test/tests/hysteresis/hys_order_05.i)
- (modules/porous_flow/test/tests/hysteresis/hys_order_08.i)
- (modules/porous_flow/test/tests/hysteresis/1phase.i)
- (modules/porous_flow/test/tests/hysteresis/1phase_relperm.i)
- (modules/porous_flow/test/tests/hysteresis/2phasePP.i)
- (modules/porous_flow/test/tests/hysteresis/1phase_relperm_2.i)
- (modules/porous_flow/test/tests/hysteresis/2phasePP_2.i)
(modules/porous_flow/test/tests/dirackernels/frompps.i)
# Test PorousFlowPointSourceFromPostprocessor DiracKernel
[Mesh]
  type = GeneratedMesh
  dim = 2
  bias_x = 1.1
  bias_y = 1.1
[]
[GlobalParams]
  PorousFlowDictator = dictator
[]
[Functions]
  [mass_flux_fn]
    type = PiecewiseConstant
    direction = left
    xy_data = '
      0    0
      100  -0.1
      300  0
      600  -0.1
      1400 0
      1500 0.2
      2000 0.2'
  []
[]
[Variables]
  [pp]
  []
[]
[Kernels]
  [mass0]
    type = PorousFlowMassTimeDerivative
    fluid_component = 0
    variable = pp
  []
[]
[UserObjects]
  [dictator]
    type = PorousFlowDictator
    porous_flow_vars = pp
    number_fluid_phases = 1
    number_fluid_components = 1
  []
[]
[FluidProperties]
  [simple_fluid]
    type = SimpleFluidProperties
    bulk_modulus = 2e9
    density0 = 1000
    thermal_expansion = 0
  []
[]
[Materials]
  [temperature]
    type = PorousFlowTemperature
  []
  [ppss]
    type = PorousFlow1PhaseFullySaturated
    porepressure = pp
  []
  [massfrac]
    type = PorousFlowMassFraction
  []
  [simple_fluid]
    type = PorousFlowSingleComponentFluid
    fp = simple_fluid
    phase = 0
  []
  [porosity]
    type = PorousFlowPorosityConst
    porosity = 0.2
  []
[]
[Postprocessors]
  [total_mass]
    type = PorousFlowFluidMass
    execute_on = 'initial timestep_end'
  []
  [mass_flux_in]
    type = FunctionValuePostprocessor
    function = mass_flux_fn
    execute_on = 'initial timestep_begin'
  []
[]
[Preconditioning]
  [smp]
    type = SMP
    full = true
  []
[]
[Executioner]
  type = Transient
  solve_type = Newton
  nl_abs_tol = 1e-14
  dt = 100
  end_time = 2000
[]
[Outputs]
  perf_graph = true
  csv = true
  execute_on = 'initial timestep_end'
  file_base = frompps
[]
[ICs]
  [PressureIC]
    variable = pp
    type = ConstantIC
    value = 20e6
  []
[]
[DiracKernels]
  [source]
    type = PorousFlowPointSourceFromPostprocessor
    variable = pp
    mass_flux = mass_flux_in
    point = '0.5 0.5 0'
  []
[]
(modules/porous_flow/test/tests/dirackernels/frompps.i)
# Test PorousFlowPointSourceFromPostprocessor DiracKernel
[Mesh]
  type = GeneratedMesh
  dim = 2
  bias_x = 1.1
  bias_y = 1.1
[]
[GlobalParams]
  PorousFlowDictator = dictator
[]
[Functions]
  [mass_flux_fn]
    type = PiecewiseConstant
    direction = left
    xy_data = '
      0    0
      100  -0.1
      300  0
      600  -0.1
      1400 0
      1500 0.2
      2000 0.2'
  []
[]
[Variables]
  [pp]
  []
[]
[Kernels]
  [mass0]
    type = PorousFlowMassTimeDerivative
    fluid_component = 0
    variable = pp
  []
[]
[UserObjects]
  [dictator]
    type = PorousFlowDictator
    porous_flow_vars = pp
    number_fluid_phases = 1
    number_fluid_components = 1
  []
[]
[FluidProperties]
  [simple_fluid]
    type = SimpleFluidProperties
    bulk_modulus = 2e9
    density0 = 1000
    thermal_expansion = 0
  []
[]
[Materials]
  [temperature]
    type = PorousFlowTemperature
  []
  [ppss]
    type = PorousFlow1PhaseFullySaturated
    porepressure = pp
  []
  [massfrac]
    type = PorousFlowMassFraction
  []
  [simple_fluid]
    type = PorousFlowSingleComponentFluid
    fp = simple_fluid
    phase = 0
  []
  [porosity]
    type = PorousFlowPorosityConst
    porosity = 0.2
  []
[]
[Postprocessors]
  [total_mass]
    type = PorousFlowFluidMass
    execute_on = 'initial timestep_end'
  []
  [mass_flux_in]
    type = FunctionValuePostprocessor
    function = mass_flux_fn
    execute_on = 'initial timestep_begin'
  []
[]
[Preconditioning]
  [smp]
    type = SMP
    full = true
  []
[]
[Executioner]
  type = Transient
  solve_type = Newton
  nl_abs_tol = 1e-14
  dt = 100
  end_time = 2000
[]
[Outputs]
  perf_graph = true
  csv = true
  execute_on = 'initial timestep_end'
  file_base = frompps
[]
[ICs]
  [PressureIC]
    variable = pp
    type = ConstantIC
    value = 20e6
  []
[]
[DiracKernels]
  [source]
    type = PorousFlowPointSourceFromPostprocessor
    variable = pp
    mass_flux = mass_flux_in
    point = '0.5 0.5 0'
  []
[]
(modules/combined/examples/geochem-porous_flow/forge/porous_flow.i)
# Input file modified from RobPodgorney version
# - 2D instead of 3D with different resolution.  Effectively this means a 1m height of RobPodgorney aquifer is simulated.  RobPodgorney total mass flux is 2.5kg/s meaning 0.25kg/s is appropriate here
# - Celsius instead of Kelvin
# - no use of PorousFlowPointEnthalpySourceFromPostprocessor since that is not yet merged into MOOSE: a DirichletBC is used instead
# - Use of PorousFlowFullySaturated instead of PorousFlowUnsaturated, and the save_component_rate_in feature to record the change in kg of each species at each node for passing to the Geochem simulation
# - MultiApps and Transfers to transfer information between this simulation and the aquifer_geochemistry.i simulation
[Mesh]
  [gen]
    type = GeneratedMeshGenerator
    dim = 2
    nx = 225
    ny = 200
    xmin = -400
    xmax = 500
    ymin = -400
    ymax = 400
  []
  [injection_node]
    input = gen
    type = ExtraNodesetGenerator
    new_boundary = injection_node
    coord = '0 0 0'
  []
[]
[GlobalParams]
  PorousFlowDictator = dictator
  gravity = '0 0 0'
[]
[Variables]
  [f_H]
    initial_condition = 8.201229858451E-07
  []
  [f_Na]
    initial_condition = 2.281094143525E-03
  []
  [f_K]
    initial_condition = 2.305489507836E-04
  []
  [f_Ca]
    initial_condition = 5.818776782059E-04
  []
  [f_Mg]
    initial_condition = 1.539513498238E-07
  []
  [f_SiO2]
    initial_condition = 2.691822196469E-04
  []
  [f_Al]
    initial_condition = 4.457519474122E-08
  []
  [f_Cl]
    initial_condition = 4.744309776594E-03
  []
  [f_SO4]
    initial_condition = 9.516650880811E-06
  []
  [f_HCO3]
    initial_condition = 5.906126982324E-05
  []
  [porepressure]
    initial_condition = 20E6
  []
  [temperature]
    initial_condition = 220 # degC
    scaling = 1E-6 # fluid enthalpy is roughly 1E6
  []
[]
[BCs]
  [source_temperature]
    type = DirichletBC
    boundary = injection_node
    variable = temperature
    value = 70 # degC
  []
[]
[DiracKernels]
  [inject_H]
    type = PorousFlowPointSourceFromPostprocessor
    point = ' 0 0 0'
    mass_flux = 4.790385871045E-08
    variable = f_H
  []
  [inject_Na]
    type = PorousFlowPointSourceFromPostprocessor
    point = ' 0 0 0'
    mass_flux = 7.586252963780E-07
    variable = f_Na
  []
  [inject_K]
    type = PorousFlowPointSourceFromPostprocessor
    point = ' 0 0 0'
    mass_flux = 2.746517625125E-07
    variable = f_K
  []
  [inject_Ca]
    type = PorousFlowPointSourceFromPostprocessor
    point = ' 0 0 0'
    mass_flux = 7.775129478597E-07
    variable = f_Ca
  []
  [inject_Mg]
    type = PorousFlowPointSourceFromPostprocessor
    point = ' 0 0 0'
    mass_flux = 1.749872109005E-07
    variable = f_Mg
  []
  [inject_SiO2]
    type = PorousFlowPointSourceFromPostprocessor
    point = ' 0 0 0'
    mass_flux = 4.100547515915E-06
    variable = f_SiO2
  []
  [inject_Al]
    type = PorousFlowPointSourceFromPostprocessor
    point = ' 0 0 0'
    mass_flux = 2.502408592080E-08
    variable = f_Al
  []
  [inject_Cl]
    type = PorousFlowPointSourceFromPostprocessor
    point = ' 0 0 0'
    mass_flux = 1.997260386272E-06
    variable = f_Cl
  []
  [inject_SO4]
    type = PorousFlowPointSourceFromPostprocessor
    point = ' 0 0 0'
    mass_flux = 2.497372164191E-07
    variable = f_SO4
  []
  [inject_HCO3]
    type = PorousFlowPointSourceFromPostprocessor
    point = ' 0 0 0'
    mass_flux = 5.003150992902E-06
    variable = f_HCO3
  []
  [inject_H2O]
    type = PorousFlowPointSourceFromPostprocessor
    point = ' 0 0 0'
    mass_flux = 2.499865905987E-01
    variable = porepressure
  []
  [produce_H]
    type = PorousFlowPeacemanBorehole
    variable = f_H
    SumQuantityUO = produced_mass_H
    mass_fraction_component = 0
    point_file = production.bh
    line_length = 1
    bottom_p_or_t = 20E6
    unit_weight = '0 0 0'
    use_mobility = true
    character = 1
  []
  [produce_Na]
    type = PorousFlowPeacemanBorehole
    variable = f_Na
    SumQuantityUO = produced_mass_Na
    mass_fraction_component = 1
    point_file = production.bh
    line_length = 1
    bottom_p_or_t = 20E6
    unit_weight = '0 0 0'
    use_mobility = true
    character = 1
  []
  [produce_K]
    type = PorousFlowPeacemanBorehole
    variable = f_K
    SumQuantityUO = produced_mass_K
    mass_fraction_component = 2
    point_file = production.bh
    line_length = 1
    bottom_p_or_t = 20E6
    unit_weight = '0 0 0'
    use_mobility = true
    character = 1
  []
  [produce_Ca]
    type = PorousFlowPeacemanBorehole
    variable = f_Ca
    SumQuantityUO = produced_mass_Ca
    mass_fraction_component = 3
    point_file = production.bh
    line_length = 1
    bottom_p_or_t = 20E6
    unit_weight = '0 0 0'
    use_mobility = true
    character = 1
  []
  [produce_Mg]
    type = PorousFlowPeacemanBorehole
    variable = f_Mg
    SumQuantityUO = produced_mass_Mg
    mass_fraction_component = 4
    point_file = production.bh
    line_length = 1
    bottom_p_or_t = 20E6
    unit_weight = '0 0 0'
    use_mobility = true
    character = 1
  []
  [produce_SiO2]
    type = PorousFlowPeacemanBorehole
    variable = f_SiO2
    SumQuantityUO = produced_mass_SiO2
    mass_fraction_component = 5
    point_file = production.bh
    line_length = 1
    bottom_p_or_t = 20E6
    unit_weight = '0 0 0'
    use_mobility = true
    character = 1
  []
  [produce_Al]
    type = PorousFlowPeacemanBorehole
    variable = f_Al
    SumQuantityUO = produced_mass_Al
    mass_fraction_component = 6
    point_file = production.bh
    line_length = 1
    bottom_p_or_t = 20E6
    unit_weight = '0 0 0'
    use_mobility = true
    character = 1
  []
  [produce_Cl]
    type = PorousFlowPeacemanBorehole
    variable = f_Cl
    SumQuantityUO = produced_mass_Cl
    mass_fraction_component = 7
    point_file = production.bh
    line_length = 1
    bottom_p_or_t = 20E6
    unit_weight = '0 0 0'
    use_mobility = true
    character = 1
  []
  [produce_SO4]
    type = PorousFlowPeacemanBorehole
    variable = f_SO4
    SumQuantityUO = produced_mass_SO4
    mass_fraction_component = 8
    point_file = production.bh
    line_length = 1
    bottom_p_or_t = 20E6
    unit_weight = '0 0 0'
    use_mobility = true
    character = 1
  []
  [produce_HCO3]
    type = PorousFlowPeacemanBorehole
    variable = f_HCO3
    SumQuantityUO = produced_mass_HCO3
    mass_fraction_component = 9
    point_file = production.bh
    line_length = 1
    bottom_p_or_t = 20E6
    unit_weight = '0 0 0'
    use_mobility = true
    character = 1
  []
  [produce_H2O]
    type = PorousFlowPeacemanBorehole
    variable = porepressure
    SumQuantityUO = produced_mass_H2O
    mass_fraction_component = 10
    point_file = production.bh
    line_length = 1
    bottom_p_or_t = 20E6
    unit_weight = '0 0 0'
    use_mobility = true
    character = 1
  []
  [remove_heat_at_production_well]
    type = PorousFlowPeacemanBorehole
    variable = temperature
    SumQuantityUO = produced_heat
    point_file = production.bh
    line_length = 1
    bottom_p_or_t = 20E6
    unit_weight = '0 0 0'
    use_mobility = true
    use_enthalpy = true
    character = 1
  []
[]
[UserObjects]
  [produced_mass_H]
    type = PorousFlowSumQuantity
  []
  [produced_mass_Na]
    type = PorousFlowSumQuantity
  []
  [produced_mass_K]
    type = PorousFlowSumQuantity
  []
  [produced_mass_Ca]
    type = PorousFlowSumQuantity
  []
  [produced_mass_Mg]
    type = PorousFlowSumQuantity
  []
  [produced_mass_SiO2]
    type = PorousFlowSumQuantity
  []
  [produced_mass_Al]
    type = PorousFlowSumQuantity
  []
  [produced_mass_Cl]
    type = PorousFlowSumQuantity
  []
  [produced_mass_SO4]
    type = PorousFlowSumQuantity
  []
  [produced_mass_HCO3]
    type = PorousFlowSumQuantity
  []
  [produced_mass_H2O]
    type = PorousFlowSumQuantity
  []
  [produced_heat]
    type = PorousFlowSumQuantity
  []
[]
[Postprocessors]
  [heat_extracted]
    type = PorousFlowPlotQuantity
    uo = produced_heat
  []
  [approx_production_temperature]
    type = PointValue
    point = '100 0 0'
    variable = temperature
  []
  [mass_extracted_H]
    type = PorousFlowPlotQuantity
    uo = produced_mass_H
    execute_on = 'initial timestep_end'
  []
  [mass_extracted_Na]
    type = PorousFlowPlotQuantity
    uo = produced_mass_Na
    execute_on = 'initial timestep_end'
  []
  [mass_extracted_K]
    type = PorousFlowPlotQuantity
    uo = produced_mass_K
    execute_on = 'initial timestep_end'
  []
  [mass_extracted_Ca]
    type = PorousFlowPlotQuantity
    uo = produced_mass_Ca
    execute_on = 'initial timestep_end'
  []
  [mass_extracted_Mg]
    type = PorousFlowPlotQuantity
    uo = produced_mass_Mg
    execute_on = 'initial timestep_end'
  []
  [mass_extracted_SiO2]
    type = PorousFlowPlotQuantity
    uo = produced_mass_SiO2
    execute_on = 'initial timestep_end'
  []
  [mass_extracted_Al]
    type = PorousFlowPlotQuantity
    uo = produced_mass_Al
    execute_on = 'initial timestep_end'
  []
  [mass_extracted_Cl]
    type = PorousFlowPlotQuantity
    uo = produced_mass_Cl
    execute_on = 'initial timestep_end'
  []
  [mass_extracted_SO4]
    type = PorousFlowPlotQuantity
    uo = produced_mass_SO4
    execute_on = 'initial timestep_end'
  []
  [mass_extracted_HCO3]
    type = PorousFlowPlotQuantity
    uo = produced_mass_HCO3
    execute_on = 'initial timestep_end'
  []
  [mass_extracted_H2O]
    type = PorousFlowPlotQuantity
    uo = produced_mass_H2O
    execute_on = 'initial timestep_end'
  []
  [mass_extracted]
    type = LinearCombinationPostprocessor
    pp_names = 'mass_extracted_H mass_extracted_Na mass_extracted_K mass_extracted_Ca mass_extracted_Mg mass_extracted_SiO2 mass_extracted_Al mass_extracted_Cl mass_extracted_SO4 mass_extracted_HCO3 mass_extracted_H2O'
    pp_coefs = '1 1 1 1 1 1 1 1 1 1 1'
    execute_on = 'initial timestep_end'
  []
  [dt]
    type = TimestepSize
    execute_on = 'timestep_begin'
  []
[]
[FluidProperties]
  [the_simple_fluid]
    type = SimpleFluidProperties
    thermal_expansion = 2E-4
    bulk_modulus = 2E9
    viscosity = 1E-3
    density0 = 980
    cv = 4000.0
    cp = 4000.0
    porepressure_coefficient = 0
  []
[]
[PorousFlowFullySaturated]
  coupling_type = ThermoHydro
  porepressure = porepressure
  temperature = temperature
  mass_fraction_vars = 'f_H f_Na f_K f_Ca f_Mg f_SiO2 f_Al f_Cl f_SO4 f_HCO3'
  save_component_rate_in = 'rate_H rate_Na rate_K rate_Ca rate_Mg rate_SiO2 rate_Al rate_Cl rate_SO4 rate_HCO3 rate_H2O' # change in kg at every node / dt
  fp = the_simple_fluid
  temperature_unit = Celsius
[]
[AuxVariables]
  [rate_H]
  []
  [rate_Na]
  []
  [rate_K]
  []
  [rate_Ca]
  []
  [rate_Mg]
  []
  [rate_SiO2]
  []
  [rate_Al]
  []
  [rate_Cl]
  []
  [rate_SO4]
  []
  [rate_HCO3]
  []
  [rate_H2O]
  []
[]
[Materials]
  [porosity]
    type = PorousFlowPorosityConst
    porosity = 0.01
  []
  [permeability]
    type = PorousFlowPermeabilityConst
    permeability = '1E-14 0 0   0 1E-14 0   0 0 1E-14'
  []
  [thermal_conductivity]
    type = PorousFlowThermalConductivityIdeal
    dry_thermal_conductivity = '2.5 0 0  0 2.5 0  0 0 2.5'
  []
  [rock_heat]
    type = PorousFlowMatrixInternalEnergy
    density = 2750.0
    specific_heat_capacity = 900.0
  []
[]
[Preconditioning]
  active = typically_efficient
  [typically_efficient]
    type = SMP
    full = true
    petsc_options_iname = '-pc_type -pc_hypre_type'
    petsc_options_value = ' hypre    boomeramg'
  []
  [strong]
    type = SMP
    full = true
    petsc_options = '-ksp_diagonal_scale -ksp_diagonal_scale_fix'
    petsc_options_iname = '-pc_type -sub_pc_type -sub_pc_factor_shift_type -pc_asm_overlap'
    petsc_options_value = ' asm      ilu           NONZERO                   2'
  []
  [probably_too_strong]
    type = SMP
    full = true
    petsc_options_iname = '-pc_type -pc_factor_mat_solver_package'
    petsc_options_value = ' lu       mumps'
  []
[]
[Executioner]
  type = Transient
  solve_type = Newton
  end_time = 31536000 #1 year
  [TimeStepper]
    type = SolutionTimeAdaptiveDT
    dt = 500
  []
[]
[Outputs]
  exodus = true
  csv = true
[]
[MultiApps]
  [react]
    type = TransientMultiApp
    input_files = aquifer_geochemistry.i
    clone_master_mesh = true
    execute_on = 'timestep_end'
  []
[]
[Transfers]
  [changes_due_to_flow]
    type = MultiAppCopyTransfer
    source_variable = 'rate_H rate_Na rate_K rate_Ca rate_Mg rate_SiO2 rate_Al rate_Cl rate_SO4 rate_HCO3 rate_H2O temperature'
    variable = 'pf_rate_H pf_rate_Na pf_rate_K pf_rate_Ca pf_rate_Mg pf_rate_SiO2 pf_rate_Al pf_rate_Cl pf_rate_SO4 pf_rate_HCO3 pf_rate_H2O temperature'
    to_multi_app = react
  []
  [massfrac_from_geochem]
    type = MultiAppCopyTransfer
    source_variable = 'massfrac_H massfrac_Na massfrac_K massfrac_Ca massfrac_Mg massfrac_SiO2 massfrac_Al massfrac_Cl massfrac_SO4 massfrac_HCO3'
    variable = 'f_H f_Na f_K f_Ca f_Mg f_SiO2 f_Al f_Cl f_SO4 f_HCO3'
    from_multi_app = react
  []
[]
(modules/porous_flow/test/tests/hysteresis/2phasePS.i)
# Simple example of a 2-phase situation with hysteretic capillary pressure.  Gas is added to and removed from the system in order to observe the hysteresis
# All liquid water exists in component 0
# All gas exists in component 1
[Mesh]
  [mesh]
    type = GeneratedMeshGenerator
    dim = 1
  []
[]
[GlobalParams]
  PorousFlowDictator = dictator
[]
[UserObjects]
  [dictator]
    type = PorousFlowDictator
    number_fluid_phases = 2
    number_fluid_components = 2
    porous_flow_vars = 'pp0 sat1'
  []
[]
[Variables]
  [pp0]
  []
  [sat1]
    initial_condition = 0
  []
[]
[Kernels]
  [mass_conservation0]
    type = PorousFlowMassTimeDerivative
    fluid_component = 0
    variable = pp0
  []
  [mass_conservation1]
    type = PorousFlowMassTimeDerivative
    fluid_component = 1
    variable = sat1
  []
[]
[DiracKernels]
  [pump]
    type = PorousFlowPointSourceFromPostprocessor
    mass_flux = flux
    point = '0.5 0 0'
    variable = sat1
  []
[]
[AuxVariables]
  [massfrac_ph0_sp0]
    initial_condition = 1
  []
  [massfrac_ph1_sp0]
    initial_condition = 0
  []
  [sat0]
    family = MONOMIAL
    order = CONSTANT
  []
  [pp1]
    family = MONOMIAL
    order = CONSTANT
  []
  [hys_order]
    family = MONOMIAL
    order = CONSTANT
  []
[]
[AuxKernels]
  [sat0]
    type = PorousFlowPropertyAux
    variable = sat0
    phase = 0
    property = saturation
  []
  [pp1]
    type = PorousFlowPropertyAux
    variable = pp1
    phase = 1
    property = pressure
  []
  [hys_order]
    type = PorousFlowPropertyAux
    variable = hys_order
    property = hysteresis_order
  []
[]
[FluidProperties]
  [simple_fluid] # same properties used for both phases
    type = SimpleFluidProperties
    bulk_modulus = 10 # so pumping does not result in excessive porepressure
  []
[]
[Materials]
  [porosity]
    type = PorousFlowPorosityConst
    porosity = 0.1
  []
  [temperature]
    type = PorousFlowTemperature
    temperature = 20
  []
  [massfrac]
    type = PorousFlowMassFraction
    mass_fraction_vars = 'massfrac_ph0_sp0 massfrac_ph1_sp0'
  []
  [simple_fluid0]
    type = PorousFlowSingleComponentFluid
    fp = simple_fluid
    phase = 0
  []
  [simple_fluid1]
    type = PorousFlowSingleComponentFluid
    fp = simple_fluid
    phase = 1
  []
  [hys_order_material]
    type = PorousFlowHysteresisOrder
  []
  [pc_calculator]
    type = PorousFlow2PhaseHysPS
    alpha_d = 10.0
    alpha_w = 7.0
    n_d = 1.5
    n_w = 1.9
    S_l_min = 0.1
    S_lr = 0.2
    S_gr_max = 0.3
    Pc_max = 12.0
    high_ratio = 0.9
    low_extension_type = quadratic
    high_extension_type = power
    phase0_porepressure = pp0
    phase1_saturation = sat1
  []
[]
[Postprocessors]
  [flux]
    type = FunctionValuePostprocessor
    function = 'if(t <= 9, 10, -10)'
  []
  [hys_order]
    type = PointValue
    point = '0 0 0'
    variable = hys_order
  []
  [sat0]
    type = PointValue
    point = '0 0 0'
    variable = sat0
  []
  [sat1]
    type = PointValue
    point = '0 0 0'
    variable = sat1
  []
  [pp0]
    type = PointValue
    point = '0 0 0'
    variable = pp0
  []
  [pp1]
    type = PointValue
    point = '0 0 0'
    variable = pp1
  []
[]
[Preconditioning]
  [smp]
    type = SMP
    full = true
    petsc_options_iname = '-pc_type -pc_factor_shift_type'
    petsc_options_value = ' lu       NONZERO'
  []
[]
[Executioner]
  type = Transient
  solve_type = Newton
  dt = 0.5
  end_time = 18
  nl_abs_tol = 1E-10
[]
[Outputs]
  csv = true
[]
(modules/porous_flow/examples/multiapp_fracture_flow/3dFracture/fracture_only_aperture_changing.i)
# Cold water injection into one side of the fracture network, and production from the other side
injection_rate = 10 # kg/s
[Mesh]
  uniform_refine = 0
  [cluster34]
    type = FileMeshGenerator
    file = 'Cluster_34.exo'
  []
  [injection_node]
    type = BoundingBoxNodeSetGenerator
    input = cluster34
    bottom_left = '-1000 0 -1000'
    top_right = '1000 0.504 1000'
    new_boundary = injection_node
  []
[]
[GlobalParams]
  PorousFlowDictator = dictator
  gravity = '0 0 -9.81E-6' # Note the value, because of pressure_unit
[]
[Variables]
  [frac_P]
    scaling = 1E6
  []
  [frac_T]
    initial_condition = 473
  []
[]
[ICs]
  [frac_P]
    type = FunctionIC
    variable = frac_P
    function = insitu_pp
  []
[]
[PorousFlowFullySaturated]
  coupling_type = ThermoHydro
  porepressure = frac_P
  temperature = frac_T
  fp = water
  pressure_unit = MPa
[]
[Kernels]
  [toMatrix]
    type = PorousFlowHeatMassTransfer
    variable = frac_T
    v = transferred_matrix_T
    transfer_coefficient = heat_transfer_coefficient
    save_in = joules_per_s
  []
[]
[AuxVariables]
  [heat_transfer_coefficient]
    family = MONOMIAL
    order = CONSTANT
    initial_condition = 0.0
  []
  [transferred_matrix_T]
    initial_condition = 473
  []
  [joules_per_s]
  []
  [normal_dirn_x]
    family = MONOMIAL
    order = CONSTANT
  []
  [normal_dirn_y]
    family = MONOMIAL
    order = CONSTANT
  []
  [normal_dirn_z]
    family = MONOMIAL
    order = CONSTANT
  []
  [enclosing_element_normal_length]
    family = MONOMIAL
    order = CONSTANT
  []
  [enclosing_element_normal_thermal_cond]
    family = MONOMIAL
    order = CONSTANT
  []
  [aperture]
    family = MONOMIAL
    order = CONSTANT
  []
  [perm_times_app]
    family = MONOMIAL
    order = CONSTANT
  []
  [density]
    family = MONOMIAL
    order = CONSTANT
  []
  [viscosity]
    family = MONOMIAL
    order = CONSTANT
  []
  [insitu_pp]
  []
[]
[AuxKernels]
  [normal_dirn_x_auxk]
    type = PorousFlowElementNormal
    variable = normal_dirn_x
    component = x
  []
  [normal_dirn_y]
    type = PorousFlowElementNormal
    variable = normal_dirn_y
    component = y
  []
  [normal_dirn_z]
    type = PorousFlowElementNormal
    variable = normal_dirn_z
    component = z
  []
  [heat_transfer_coefficient_auxk]
    type = ParsedAux
    variable = heat_transfer_coefficient
    coupled_variables = 'enclosing_element_normal_length enclosing_element_normal_thermal_cond'
    constant_names = h_s
    constant_expressions = 1E3 # should be much bigger than thermal_conductivity / L ~ 1
    expression = 'if(enclosing_element_normal_length = 0, 0, h_s * enclosing_element_normal_thermal_cond * 2 * enclosing_element_normal_length / (h_s * enclosing_element_normal_length * enclosing_element_normal_length + enclosing_element_normal_thermal_cond * 2 * enclosing_element_normal_length))'
  []
  [aperture]
    type = PorousFlowPropertyAux
    variable = aperture
    property = porosity
  []
  [perm_times_app]
    type = PorousFlowPropertyAux
    variable = perm_times_app
    property = permeability
    row = 0
    column = 0
  []
  [density]
    type = PorousFlowPropertyAux
    variable = density
    property = density
    phase = 0
  []
  [viscosity]
    type = PorousFlowPropertyAux
    variable = viscosity
    property = viscosity
    phase = 0
  []
  [insitu_pp]
    type = FunctionAux
    execute_on = initial
    variable = insitu_pp
    function = insitu_pp
  []
[]
[BCs]
  [inject_heat]
    type = DirichletBC
    boundary = injection_node
    variable = frac_T
    value = 373
  []
[]
[DiracKernels]
  [inject_fluid]
    type = PorousFlowPointSourceFromPostprocessor
    mass_flux = ${injection_rate}
    point = '58.8124 0.50384 74.7838'
    variable = frac_P
  []
  [withdraw_fluid]
    type = PorousFlowPeacemanBorehole
    SumQuantityUO = kg_out_uo
    bottom_p_or_t = 10.6 # 1MPa + approx insitu at production point, to prevent aperture closing due to low porepressures
    character = 1
    line_length = 1
    point_file = production.xyz
    unit_weight = '0 0 0'
    fluid_phase = 0
    use_mobility = true
    variable = frac_P
  []
  [withdraw_heat]
    type = PorousFlowPeacemanBorehole
    SumQuantityUO = J_out_uo
    bottom_p_or_t = 10.6 # 1MPa + approx insitu at production point, to prevent aperture closing due to low porepressures
    character = 1
    line_length = 1
    point_file = production.xyz
    unit_weight = '0 0 0'
    fluid_phase = 0
    use_mobility = true
    use_enthalpy = true
    variable = frac_T
  []
[]
[UserObjects]
  [kg_out_uo]
    type = PorousFlowSumQuantity
  []
  [J_out_uo]
    type = PorousFlowSumQuantity
  []
[]
[FluidProperties]
  [true_water]
    type = Water97FluidProperties
  []
  [water]
    type = TabulatedBicubicFluidProperties
    fp = true_water
    temperature_min = 275 # K
    temperature_max = 600
    interpolated_properties = 'density viscosity enthalpy internal_energy'
    fluid_property_output_file = water97_tabulated.csv
    # Comment out the fp parameter and uncomment below to use the newly generated tabulation
    # fluid_property_file = water97_tabulated.csv
  []
[]
[Materials]
  [porosity]
    type = PorousFlowPorosityLinear
    porosity_ref = 1E-4 # fracture porosity = 1.0, but must include fracture aperture of 1E-4 at P = insitu_pp
    P_ref = insitu_pp
    P_coeff = 1E-3 # this is in metres/MPa, ie for P_ref = 1/P_coeff, the aperture becomes 1 metre
    porosity_min = 1E-5
  []
  [permeability]
    type = PorousFlowPermeabilityKozenyCarman
    k0 = 1E-15 # fracture perm = 1E-11 m^2, but must include fracture aperture of 1E-4
    poroperm_function = kozeny_carman_phi0
    m = 0
    n = 3
    phi0 = 1E-4
  []
  [internal_energy]
    type = PorousFlowMatrixInternalEnergy
    density = 2700 # kg/m^3
    specific_heat_capacity = 0 # basically no rock inside the fracture
  []
  [aq_thermal_conductivity]
    type = PorousFlowThermalConductivityIdeal
    dry_thermal_conductivity = '0.6E-4 0 0  0 0.6E-4 0  0 0 0.6E-4' # thermal conductivity of water times fracture aperture.  This should increase linearly with aperture, but is set constant in this model
  []
[]
[Functions]
  [kg_rate]
    type = ParsedFunction
    symbol_values = 'dt kg_out'
    symbol_names = 'dt kg_out'
    expression = 'kg_out/dt'
  []
  [insitu_pp]
    type = ParsedFunction
    expression = '10 - 0.847E-2 * z' # Approximate hydrostatic in MPa
  []
[]
[Postprocessors]
  [dt]
    type = TimestepSize
    outputs = 'none'
  []
  [kg_out]
    type = PorousFlowPlotQuantity
    uo = kg_out_uo
  []
  [kg_per_s]
    type = FunctionValuePostprocessor
    function = kg_rate
  []
  [J_out]
    type = PorousFlowPlotQuantity
    uo = J_out_uo
  []
  [TK_out]
    type = PointValue
    variable = frac_T
    point = '101.705 160.459 39.5722'
  []
  [P_out]
    type = PointValue
    variable = frac_P
    point = '101.705 160.459 39.5722'
  []
  [P_in]
    type = PointValue
    variable = frac_P
    point = '58.8124 0.50384 74.7838'
  []
[]
[VectorPostprocessors]
  [heat_transfer_rate]
    type = NodalValueSampler
    outputs = none
    sort_by = id
    variable = joules_per_s
  []
[]
[Preconditioning]
  [entire_jacobian]
    type = SMP
    full = true
    petsc_options_iname = '-pc_type -sub_pc_type -sub_pc_factor_shift_type -pc_asm_overlap'
    petsc_options_value = ' asm      lu           NONZERO                   2             '
  []
[]
[Executioner]
  type = Transient
  solve_type = NEWTON
  [TimeStepper]
    type = IterationAdaptiveDT
    dt = 1
    optimal_iterations = 10
    growth_factor = 1.5
  []
  dtmax = 1E8
  end_time = 1E8
  nl_abs_tol = 1E-3
  nl_max_its = 20
[]
[Outputs]
  print_linear_residuals = false
  csv = true
  [ex]
    type = Exodus
    sync_times = '1 10 100 200 300 400 500 600 700 800 900 1000 1100 1200 1300 1400 1500 1600 1700 1800 1900 2000 2100 2200 2300 2400 2500 2600 2700 2800 2900 3000 3100 3200 3300 3400 3500 3600 3700 3800 3900 4000 4100 4200 4300 4400 4500 4600 4700 4800 4900 5000 5100 5200 5300 5400 5500 5600 5700 5800 5900 6000 6100 6200 6300 6400 6500 6600 6700 6800 6900 7000 7100 7200 7300 7400 7500 7600 7700 7800 7900 8000 8100 8200 8300 8400 8500 8600 8700 8800 8900 9000 10000 11000 12000 13000 14000 15000 16000 17000 18000 19000 20000 30000 50000 70000 100000 200000 300000 400000 500000 600000 700000 800000 900000 1000000 1100000 1200000 1300000 1400000 1500000 1600000 1700000 1800000 1900000 2000000 2100000 2200000 2300000 2400000 2500000 2600000 2700000 2800000 2900000'
    sync_only = true
  []
[]
(modules/porous_flow/test/tests/hysteresis/hys_order_09.i)
# Test that PorousFlowHysteresisOrder correctly calculates hysteresis order
# Hysteresis order is initialised = 3, with turning points = (0.5, 0.8, 0.66)
# Initial saturation is 0.71
# A large amount of water is removed in one timestep so the saturation becomes 0.58 (and order = 0)
# Then, water is added to the system (order = 1, with turning point = 0.58) until saturation = 0.67
# Then, water is removed from the system so order becomes 2 with turning point = 0.67
# Then, water is removed from the system until saturation < 0.58 and order = 0
[Mesh]
  [mesh]
    type = GeneratedMeshGenerator
    dim = 1
  []
[]
[GlobalParams]
  PorousFlowDictator = dictator
[]
[Variables]
  [pp]
    initial_condition = -9E5
  []
[]
[PorousFlowUnsaturated]
  porepressure = pp
  fp = simple_fluid
[]
[DiracKernels]
  [source_sink_0]
    type = PorousFlowPointSourceFromPostprocessor
    point = '0 0 0'
    mass_flux = sink_strength
    variable = pp
  []
  [source_sink_1]
    type = PorousFlowPointSourceFromPostprocessor
    point = '1 0 0'
    mass_flux = sink_strength
    variable = pp
  []
[]
[FluidProperties]
  [simple_fluid]
    type = SimpleFluidProperties
  []
[]
[Materials]
  [porosity]
    type = PorousFlowPorosityConst
    porosity = 1.0
  []
  [permeability]
    type = PorousFlowPermeabilityConst
    permeability = '0 0 0   0 0 0   0 0 0'
  []
  [hys_order]
    type = PorousFlowHysteresisOrder
    initial_order = 3
    previous_turning_points = '0.6 0.8 0.66'
  []
[]
[AuxVariables]
  [hys_order]
    family = MONOMIAL
    order = CONSTANT
  []
  [tp0]
    family = MONOMIAL
    order = CONSTANT
  []
  [tp1]
    family = MONOMIAL
    order = CONSTANT
  []
  [tp2]
    family = MONOMIAL
    order = CONSTANT
  []
[]
[AuxKernels]
  [hys_order]
    type = PorousFlowPropertyAux
    variable = hys_order
    property = hysteresis_order
  []
  [tp0]
    type = PorousFlowPropertyAux
    variable = tp0
    property = hysteresis_saturation_turning_point
    hysteresis_turning_point = 0
  []
  [tp1]
    type = PorousFlowPropertyAux
    variable = tp1
    property = hysteresis_saturation_turning_point
    hysteresis_turning_point = 1
  []
  [tp2]
    type = PorousFlowPropertyAux
    variable = tp2
    property = hysteresis_saturation_turning_point
    hysteresis_turning_point = 2
  []
[]
[Functions]
  [sink_strength_fcn]
    type = ParsedFunction
  expression = '30 * if(t <= 1, -2, if(t <= 2, 1.5, -1))'
  []
[]
[Postprocessors]
  [sink_strength]
    type = FunctionValuePostprocessor
    function = sink_strength_fcn
    outputs = 'none'
  []
  [saturation]
    type = PointValue
    point = '0 0 0'
    variable = saturation0
  []
  [hys_order]
    type = PointValue
    point = '0 0 0'
    variable = hys_order
  []
  [tp0]
    type = PointValue
    point = '0 0 0'
    variable = tp0
  []
  [tp1]
    type = PointValue
    point = '0 0 0'
    variable = tp1
  []
  [tp2]
    type = PointValue
    point = '0 0 0'
    variable = tp2
  []
[]
[Preconditioning]
  [basic]
    type = SMP
    full = true
  []
[]
[Executioner]
  type = Transient
  solve_type = Newton
  dt = 1
  end_time = 6
  nl_abs_tol = 1E-7
[]
[Outputs]
  [csv]
    type = CSV
  []
[]
(modules/porous_flow/test/tests/hysteresis/hys_order_06.i)
# Test that PorousFlowHysteresisOrder correctly calculates hysteresis order
# Hysteresis order is initialised = 2, with turning points = (0.6, 0.8)
# Initial saturation is 0.71
# Water is added to the system, so order = 3 with turning point = 0.71
# Then water is added to the system until saturation = 0.8, when order = 1
# Then water is added to the system until saturation = 1.0, when order becomes zero
[Mesh]
  [mesh]
    type = GeneratedMeshGenerator
    dim = 1
  []
[]
[GlobalParams]
  PorousFlowDictator = dictator
[]
[Variables]
  [pp]
    initial_condition = -9E5
  []
[]
[PorousFlowUnsaturated]
  porepressure = pp
  fp = simple_fluid
[]
[DiracKernels]
  [source_sink_0]
    type = PorousFlowPointSourceFromPostprocessor
    point = '0 0 0'
    mass_flux = sink_strength
    variable = pp
  []
  [source_sink_1]
    type = PorousFlowPointSourceFromPostprocessor
    point = '1 0 0'
    mass_flux = sink_strength
    variable = pp
  []
[]
[FluidProperties]
  [simple_fluid]
    type = SimpleFluidProperties
  []
[]
[Materials]
  [porosity]
    type = PorousFlowPorosityConst
    porosity = 1.0
  []
  [permeability]
    type = PorousFlowPermeabilityConst
    permeability = '0 0 0   0 0 0   0 0 0'
  []
  [hys_order]
    type = PorousFlowHysteresisOrder
    initial_order = 2
    previous_turning_points = '0.6 0.8'
  []
[]
[AuxVariables]
  [hys_order]
    family = MONOMIAL
    order = CONSTANT
  []
  [tp0]
    family = MONOMIAL
    order = CONSTANT
  []
  [tp1]
    family = MONOMIAL
    order = CONSTANT
  []
  [tp2]
    family = MONOMIAL
    order = CONSTANT
  []
[]
[AuxKernels]
  [hys_order]
    type = PorousFlowPropertyAux
    variable = hys_order
    property = hysteresis_order
  []
  [tp0]
    type = PorousFlowPropertyAux
    variable = tp0
    property = hysteresis_saturation_turning_point
    hysteresis_turning_point = 0
  []
  [tp1]
    type = PorousFlowPropertyAux
    variable = tp1
    property = hysteresis_saturation_turning_point
    hysteresis_turning_point = 1
  []
  [tp2]
    type = PorousFlowPropertyAux
    variable = tp2
    property = hysteresis_saturation_turning_point
    hysteresis_turning_point = 2
  []
[]
[Functions]
  [sink_strength_fcn]
    type = ParsedFunction
    expression = '30'
  []
[]
[Postprocessors]
  [sink_strength]
    type = FunctionValuePostprocessor
    function = sink_strength_fcn
    outputs = 'none'
  []
  [saturation]
    type = PointValue
    point = '0 0 0'
    variable = saturation0
  []
  [hys_order]
    type = PointValue
    point = '0 0 0'
    variable = hys_order
  []
  [tp0]
    type = PointValue
    point = '0 0 0'
    variable = tp0
  []
  [tp1]
    type = PointValue
    point = '0 0 0'
    variable = tp1
  []
  [tp2]
    type = PointValue
    point = '0 0 0'
    variable = tp2
  []
[]
[Preconditioning]
  [basic]
    type = SMP
    full = true
  []
[]
[Executioner]
  type = Transient
  solve_type = Newton
  dt = 1
  end_time = 7
  nl_abs_tol = 1E-7
[]
[Outputs]
  [csv]
    type = CSV
  []
[]
(modules/porous_flow/test/tests/hysteresis/2phasePS_relperm.i)
# Simple example of a 2-phase situation with hysteretic relative permeability.  Gas is added to and removed from the system in order to observe the hysteresis
# All liquid water exists in component 0
# All gas exists in component 1
[Mesh]
  [mesh]
    type = GeneratedMeshGenerator
    dim = 1
  []
[]
[GlobalParams]
  PorousFlowDictator = dictator
[]
[UserObjects]
  [dictator]
    type = PorousFlowDictator
    number_fluid_phases = 2
    number_fluid_components = 2
    porous_flow_vars = 'pp0 sat1'
  []
  [pc]
    type = PorousFlowCapillaryPressureVG
    alpha = 10.0
    m = 0.33
  []
[]
[Variables]
  [pp0]
  []
  [sat1]
    initial_condition = 0
  []
[]
[Kernels]
  [mass_conservation0]
    type = PorousFlowMassTimeDerivative
    fluid_component = 0
    variable = pp0
  []
  [mass_conservation1]
    type = PorousFlowMassTimeDerivative
    fluid_component = 1
    variable = sat1
  []
[]
[DiracKernels]
  [pump]
    type = PorousFlowPointSourceFromPostprocessor
    mass_flux = flux
    point = '0.5 0 0'
    variable = sat1
  []
[]
[AuxVariables]
  [massfrac_ph0_sp0]
    initial_condition = 1
  []
  [massfrac_ph1_sp0]
    initial_condition = 0
  []
  [sat0]
    family = MONOMIAL
    order = CONSTANT
  []
  [pp1]
    family = MONOMIAL
    order = CONSTANT
  []
  [hys_order]
    family = MONOMIAL
    order = CONSTANT
  []
  [relperm_liquid]
    family = MONOMIAL
    order = CONSTANT
  []
  [relperm_gas]
    family = MONOMIAL
    order = CONSTANT
  []
[]
[AuxKernels]
  [sat0]
    type = PorousFlowPropertyAux
    variable = sat0
    phase = 0
    property = saturation
  []
  [relperm_liquid]
    type = PorousFlowPropertyAux
    variable = relperm_liquid
    property = relperm
    phase = 0
  []
  [relperm_gas]
    type = PorousFlowPropertyAux
    variable = relperm_gas
    property = relperm
    phase = 1
  []
  [pp1]
    type = PorousFlowPropertyAux
    variable = pp1
    phase = 1
    property = pressure
  []
  [hys_order]
    type = PorousFlowPropertyAux
    variable = hys_order
    property = hysteresis_order
  []
[]
[FluidProperties]
  [simple_fluid] # same properties used for both phases
    type = SimpleFluidProperties
    bulk_modulus = 10 # so pumping does not result in excessive porepressure
  []
[]
[Materials]
  [porosity]
    type = PorousFlowPorosityConst
    porosity = 0.1
  []
  [temperature]
    type = PorousFlowTemperature
    temperature = 20
  []
  [massfrac]
    type = PorousFlowMassFraction
    mass_fraction_vars = 'massfrac_ph0_sp0 massfrac_ph1_sp0'
  []
  [simple_fluid0]
    type = PorousFlowSingleComponentFluid
    fp = simple_fluid
    phase = 0
  []
  [simple_fluid1]
    type = PorousFlowSingleComponentFluid
    fp = simple_fluid
    phase = 1
  []
  [pc_calculator]
    type = PorousFlow2PhasePS
    capillary_pressure = pc
    phase0_porepressure = pp0
    phase1_saturation = sat1
  []
  [hys_order_material]
    type = PorousFlowHysteresisOrder
  []
  [relperm_liquid]
    type = PorousFlowHystereticRelativePermeabilityLiquid
    phase = 0
    S_lr = 0.1
    S_gr_max = 0.2
    m = 0.9
    liquid_modification_range = 0.9
  []
  [relperm_gas]
    type = PorousFlowHystereticRelativePermeabilityGas
    phase = 1
    S_lr = 0.1
    S_gr_max = 0.2
    m = 0.9
    gamma = 0.33
    k_rg_max = 0.8
    gas_low_extension_type = linear_like
  []
[]
[Postprocessors]
  [flux]
    type = FunctionValuePostprocessor
    function = 'if(t <= 9, 10, -10)'
  []
  [hys_order]
    type = PointValue
    point = '0 0 0'
    variable = hys_order
  []
  [sat0]
    type = PointValue
    point = '0 0 0'
    variable = sat0
  []
  [sat1]
    type = PointValue
    point = '0 0 0'
    variable = sat1
  []
  [kr_liq]
    type = PointValue
    point = '0 0 0'
    variable = relperm_liquid
  []
  [kr_gas]
    type = PointValue
    point = '0 0 0'
    variable = relperm_gas
  []
[]
[Preconditioning]
  [smp]
    type = SMP
    full = true
    petsc_options_iname = '-pc_type -pc_factor_shift_type'
    petsc_options_value = ' lu       NONZERO'
  []
[]
[Executioner]
  type = Transient
  solve_type = Newton
  dt = 0.5
  end_time = 18
  nl_abs_tol = 1E-10
[]
[Outputs]
  csv = true
[]
(modules/porous_flow/test/tests/hysteresis/1phase_3rd.i)
# Simple example of a 1-phase situation with hysteretic capillary pressure that involves a 3rd-order curve.  Water is removed, added, removed and added to the system in order to observe the hysteresis
[Mesh]
  [mesh]
    type = GeneratedMeshGenerator
    dim = 1
  []
[]
[GlobalParams]
  PorousFlowDictator = dictator
[]
[UserObjects]
  [dictator]
    type = PorousFlowDictator
    number_fluid_phases = 1
    number_fluid_components = 1
    porous_flow_vars = 'pp'
  []
[]
[Variables]
  [pp]
    initial_condition = 0
  []
[]
[Kernels]
  [mass_conservation]
    type = PorousFlowMassTimeDerivative
    variable = pp
  []
[]
[DiracKernels]
  [pump]
    type = PorousFlowPointSourceFromPostprocessor
    mass_flux = flux
    point = '0.5 0 0'
    variable = pp
  []
[]
[AuxVariables]
  [sat]
    family = MONOMIAL
    order = CONSTANT
  []
  [hys_order]
    family = MONOMIAL
    order = CONSTANT
  []
[]
[AuxKernels]
  [sat]
    type = PorousFlowPropertyAux
    variable = sat
    property = saturation
  []
  [hys_order]
    type = PorousFlowPropertyAux
    variable = hys_order
    property = hysteresis_order
  []
[]
[FluidProperties]
  [simple_fluid]
    type = SimpleFluidProperties
  []
[]
[Materials]
  [porosity]
    type = PorousFlowPorosityConst
    porosity = 0.1
  []
  [temperature]
    type = PorousFlowTemperature
    temperature = 20
  []
  [massfrac]
    type = PorousFlowMassFraction
  []
  [simple_fluid]
    type = PorousFlowSingleComponentFluid
    fp = simple_fluid
    phase = 0
  []
  [hys_order_material]
    type = PorousFlowHysteresisOrder
  []
  [pc_calculator]
    type = PorousFlow1PhaseHysP
    alpha_d = 10.0
    alpha_w = 7.0
    n_d = 1.5
    n_w = 1.9
    S_l_min = 0.1
    S_lr = 0.2
    S_gr_max = 0.3
    Pc_max = 12.0
    high_ratio = 0.9
    low_extension_type = quadratic
    high_extension_type = power
    porepressure = pp
  []
[]
[Postprocessors]
  [flux]
    type = FunctionValuePostprocessor
    function = 'if(t <= 9, -10, if(t <= 16, 10, if(t <= 22, -10, 10)))'
  []
  [hys_order]
    type = PointValue
    point = '0 0 0'
    variable = hys_order
  []
  [sat]
    type = PointValue
    point = '0 0 0'
    variable = sat
  []
  [pp]
    type = PointValue
    point = '0 0 0'
    variable = pp
  []
[]
[Executioner]
  type = Transient
  solve_type = Newton
  dt = 0.5
  end_time = 30.5
  nl_abs_tol = 1E-10
[]
[Outputs]
  csv = true
[]
(modules/porous_flow/test/tests/dirackernels/frompps.i)
# Test PorousFlowPointSourceFromPostprocessor DiracKernel
[Mesh]
  type = GeneratedMesh
  dim = 2
  bias_x = 1.1
  bias_y = 1.1
[]
[GlobalParams]
  PorousFlowDictator = dictator
[]
[Functions]
  [mass_flux_fn]
    type = PiecewiseConstant
    direction = left
    xy_data = '
      0    0
      100  -0.1
      300  0
      600  -0.1
      1400 0
      1500 0.2
      2000 0.2'
  []
[]
[Variables]
  [pp]
  []
[]
[Kernels]
  [mass0]
    type = PorousFlowMassTimeDerivative
    fluid_component = 0
    variable = pp
  []
[]
[UserObjects]
  [dictator]
    type = PorousFlowDictator
    porous_flow_vars = pp
    number_fluid_phases = 1
    number_fluid_components = 1
  []
[]
[FluidProperties]
  [simple_fluid]
    type = SimpleFluidProperties
    bulk_modulus = 2e9
    density0 = 1000
    thermal_expansion = 0
  []
[]
[Materials]
  [temperature]
    type = PorousFlowTemperature
  []
  [ppss]
    type = PorousFlow1PhaseFullySaturated
    porepressure = pp
  []
  [massfrac]
    type = PorousFlowMassFraction
  []
  [simple_fluid]
    type = PorousFlowSingleComponentFluid
    fp = simple_fluid
    phase = 0
  []
  [porosity]
    type = PorousFlowPorosityConst
    porosity = 0.2
  []
[]
[Postprocessors]
  [total_mass]
    type = PorousFlowFluidMass
    execute_on = 'initial timestep_end'
  []
  [mass_flux_in]
    type = FunctionValuePostprocessor
    function = mass_flux_fn
    execute_on = 'initial timestep_begin'
  []
[]
[Preconditioning]
  [smp]
    type = SMP
    full = true
  []
[]
[Executioner]
  type = Transient
  solve_type = Newton
  nl_abs_tol = 1e-14
  dt = 100
  end_time = 2000
[]
[Outputs]
  perf_graph = true
  csv = true
  execute_on = 'initial timestep_end'
  file_base = frompps
[]
[ICs]
  [PressureIC]
    variable = pp
    type = ConstantIC
    value = 20e6
  []
[]
[DiracKernels]
  [source]
    type = PorousFlowPointSourceFromPostprocessor
    variable = pp
    mass_flux = mass_flux_in
    point = '0.5 0.5 0'
  []
[]
(modules/porous_flow/test/tests/hysteresis/2phasePS_relperm_2.i)
# Simple example of a 2-phase situation with hysteretic relative permeability.  Gas is added to and removed from the system in order to observe the hysteresis
# All liquid water exists in component 0
# All gas exists in component 1
[Mesh]
  [mesh]
    type = GeneratedMeshGenerator
    dim = 1
  []
[]
[GlobalParams]
  PorousFlowDictator = dictator
[]
[UserObjects]
  [dictator]
    type = PorousFlowDictator
    number_fluid_phases = 2
    number_fluid_components = 2
    porous_flow_vars = 'pp0 sat1'
  []
  [pc]
    type = PorousFlowCapillaryPressureVG
    alpha = 10.0
    m = 0.33
  []
[]
[Variables]
  [pp0]
  []
  [sat1]
    initial_condition = 0
  []
[]
[Kernels]
  [mass_conservation0]
    type = PorousFlowMassTimeDerivative
    fluid_component = 0
    variable = pp0
  []
  [mass_conservation1]
    type = PorousFlowMassTimeDerivative
    fluid_component = 1
    variable = sat1
  []
[]
[DiracKernels]
  [pump]
    type = PorousFlowPointSourceFromPostprocessor
    mass_flux = flux
    point = '0.5 0 0'
    variable = sat1
  []
[]
[AuxVariables]
  [massfrac_ph0_sp0]
    initial_condition = 1
  []
  [massfrac_ph1_sp0]
    initial_condition = 0
  []
  [sat0]
    family = MONOMIAL
    order = CONSTANT
  []
  [pp1]
    family = MONOMIAL
    order = CONSTANT
  []
  [hys_order]
    family = MONOMIAL
    order = CONSTANT
  []
  [relperm_liquid]
    family = MONOMIAL
    order = CONSTANT
  []
  [relperm_gas]
    family = MONOMIAL
    order = CONSTANT
  []
[]
[AuxKernels]
  [sat0]
    type = PorousFlowPropertyAux
    variable = sat0
    phase = 0
    property = saturation
  []
  [relperm_liquid]
    type = PorousFlowPropertyAux
    variable = relperm_liquid
    property = relperm
    phase = 0
  []
  [relperm_gas]
    type = PorousFlowPropertyAux
    variable = relperm_gas
    property = relperm
    phase = 1
  []
  [pp1]
    type = PorousFlowPropertyAux
    variable = pp1
    phase = 1
    property = pressure
  []
  [hys_order]
    type = PorousFlowPropertyAux
    variable = hys_order
    property = hysteresis_order
  []
[]
[FluidProperties]
  [simple_fluid] # same properties used for both phases
    type = SimpleFluidProperties
    bulk_modulus = 10 # so pumping does not result in excessive porepressure
  []
[]
[Materials]
  [porosity]
    type = PorousFlowPorosityConst
    porosity = 0.1
  []
  [temperature]
    type = PorousFlowTemperature
    temperature = 20
  []
  [massfrac]
    type = PorousFlowMassFraction
    mass_fraction_vars = 'massfrac_ph0_sp0 massfrac_ph1_sp0'
  []
  [simple_fluid0]
    type = PorousFlowSingleComponentFluid
    fp = simple_fluid
    phase = 0
  []
  [simple_fluid1]
    type = PorousFlowSingleComponentFluid
    fp = simple_fluid
    phase = 1
  []
  [pc_calculator]
    type = PorousFlow2PhasePS
    capillary_pressure = pc
    phase0_porepressure = pp0
    phase1_saturation = sat1
  []
  [hys_order_material]
    type = PorousFlowHysteresisOrder
  []
  [relperm_liquid]
    type = PorousFlowHystereticRelativePermeabilityLiquid
    phase = 0
    S_lr = 0.4
    S_gr_max = 0.2
    m = 0.9
    liquid_modification_range = 0.9
  []
  [relperm_gas]
    type = PorousFlowHystereticRelativePermeabilityGas
    phase = 1
    S_lr = 0.4
    S_gr_max = 0.2
    m = 0.9
    gamma = 0.33
    k_rg_max = 1.0
    gas_low_extension_type = linear_like
  []
[]
[Postprocessors]
  [flux]
    type = FunctionValuePostprocessor
    function = 'if(t <= 15, 20, -20)'
  []
  [hys_order]
    type = PointValue
    point = '0 0 0'
    variable = hys_order
  []
  [sat0]
    type = PointValue
    point = '0 0 0'
    variable = sat0
  []
  [sat1]
    type = PointValue
    point = '0 0 0'
    variable = sat1
  []
  [kr_liq]
    type = PointValue
    point = '0 0 0'
    variable = relperm_liquid
  []
  [kr_gas]
    type = PointValue
    point = '0 0 0'
    variable = relperm_gas
  []
[]
[Preconditioning]
  [smp]
    type = SMP
    full = true
    petsc_options_iname = '-pc_type -pc_factor_shift_type'
    petsc_options_value = ' lu       NONZERO'
  []
[]
[Executioner]
  type = Transient
  solve_type = Newton
  dt = 5
  end_time = 29
  nl_abs_tol = 1E-10
[]
[Outputs]
  [csv]
    type = CSV
    sync_times = '0 1 2 3 8 12 13 14 15 16 17 18 20 24 25 26 27 28 29'
    sync_only = true
    file_base = '2phasePS_relperm_2_none'
  []
[]
(modules/porous_flow/test/tests/hysteresis/hys_order_03.i)
# Test that PorousFlowHysteresisOrder correctly calculates hysteresis order
# Water is removed from the system (so order = 0) until saturation = 0.49
# Then, water is added to the system (so order = 1) until saturation = 0.94
# Then, water is removed from the system (so order = 2) until saturation = 0.62
# Then, water is added to the system (so order = 3) until saturation = 0.87
# Then, water is removed from the system (so order = 3, because max_order = 3) until saturation = 0.68
# Then, water is added to the system (so order = 3, because max_order = 3) until saturation = 0.87
# Then, water is removed from the system (so order = 3, because max_order = 3) until saturation = 0.62
# Then, water is removed from the system (so order = 2) until saturation = 0.49
# Then, water is removed from the system (so order = 0)
[Mesh]
  [mesh]
    type = GeneratedMeshGenerator
    dim = 1
  []
[]
[GlobalParams]
  PorousFlowDictator = dictator
[]
[Variables]
  [pp]
    initial_condition = 0.0
  []
[]
[PorousFlowUnsaturated]
  porepressure = pp
  fp = simple_fluid
[]
[DiracKernels]
  [source_sink_0]
    type = PorousFlowPointSourceFromPostprocessor
    point = '0 0 0'
    mass_flux = sink_strength
    variable = pp
  []
  [source_sink_1]
    type = PorousFlowPointSourceFromPostprocessor
    point = '1 0 0'
    mass_flux = sink_strength
    variable = pp
  []
[]
[FluidProperties]
  [simple_fluid]
    type = SimpleFluidProperties
  []
[]
[Materials]
  [porosity]
    type = PorousFlowPorosityConst
    porosity = 1.0
  []
  [permeability]
    type = PorousFlowPermeabilityConst
    permeability = '0 0 0   0 0 0   0 0 0'
  []
  [hys_order]
    type = PorousFlowHysteresisOrder
  []
[]
[AuxVariables]
  [hys_order]
    family = MONOMIAL
    order = CONSTANT
  []
  [tp0]
    family = MONOMIAL
    order = CONSTANT
  []
  [tp1]
    family = MONOMIAL
    order = CONSTANT
  []
  [tp2]
    family = MONOMIAL
    order = CONSTANT
  []
[]
[AuxKernels]
  [hys_order]
    type = PorousFlowPropertyAux
    variable = hys_order
    property = hysteresis_order
  []
  [tp0]
    type = PorousFlowPropertyAux
    variable = tp0
    property = hysteresis_saturation_turning_point
    hysteresis_turning_point = 0
  []
  [tp1]
    type = PorousFlowPropertyAux
    variable = tp1
    property = hysteresis_saturation_turning_point
    hysteresis_turning_point = 1
  []
  [tp2]
    type = PorousFlowPropertyAux
    variable = tp2
    property = hysteresis_saturation_turning_point
    hysteresis_turning_point = 2
  []
[]
[Functions]
  [sink_strength_fcn]
    type = ParsedFunction
    expression = '30 * if(t <= 8, -1, if(t <= 15, 1, if(t <= 20, -1, if(t <= 24, 1, if(t <= 27, -1, if(t <= 30, 1, -1))))))'
  []
[]
[Postprocessors]
  [sink_strength]
    type = FunctionValuePostprocessor
    function = sink_strength_fcn
    outputs = 'none'
  []
  [saturation]
    type = PointValue
    point = '0 0 0'
    variable = saturation0
  []
  [hys_order]
    type = PointValue
    point = '0 0 0'
    variable = hys_order
  []
  [tp0]
    type = PointValue
    point = '0 0 0'
    variable = tp0
  []
  [tp1]
    type = PointValue
    point = '0 0 0'
    variable = tp1
  []
  [tp2]
    type = PointValue
    point = '0 0 0'
    variable = tp2
  []
[]
[Preconditioning]
  [basic]
    type = SMP
    full = true
  []
[]
[Executioner]
  type = Transient
  solve_type = Newton
  dt = 1
  end_time = 40
  nl_abs_tol = 1E-7
[]
[Outputs]
  [csv]
    type = CSV
    sync_times = '0 1 2 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 37 40' # cut out the times around which order reductions occur becuase numerical roundoff might mean order is not reduced exactly at these times
    sync_only = true
  []
[]
(modules/porous_flow/test/tests/hysteresis/hys_order_04.i)
# Test that PorousFlowHysteresisOrder correctly calculates hysteresis order
# Hysteresis order is initialised = 3, with turning points = (0.5, 0.9, 0.6)
# Initial saturation is 0.71
# Water is removed from the system (so order = 3) until saturation = 0.6
# Water is removed from the system (so order = 2) until saturation = 0.5
# Water is removed from the system (so order = 0)
[Mesh]
  [mesh]
    type = GeneratedMeshGenerator
    dim = 1
  []
[]
[GlobalParams]
  PorousFlowDictator = dictator
[]
[Variables]
  [pp]
    initial_condition = -9E5
  []
[]
[PorousFlowUnsaturated]
  porepressure = pp
  fp = simple_fluid
[]
[DiracKernels]
  [source_sink_0]
    type = PorousFlowPointSourceFromPostprocessor
    point = '0 0 0'
    mass_flux = sink_strength
    variable = pp
  []
  [source_sink_1]
    type = PorousFlowPointSourceFromPostprocessor
    point = '1 0 0'
    mass_flux = sink_strength
    variable = pp
  []
[]
[FluidProperties]
  [simple_fluid]
    type = SimpleFluidProperties
  []
[]
[Materials]
  [porosity]
    type = PorousFlowPorosityConst
    porosity = 1.0
  []
  [permeability]
    type = PorousFlowPermeabilityConst
    permeability = '0 0 0   0 0 0   0 0 0'
  []
  [hys_order]
    type = PorousFlowHysteresisOrder
    initial_order = 3
    previous_turning_points = '0.5 0.9 0.6'
  []
[]
[AuxVariables]
  [hys_order]
    family = MONOMIAL
    order = CONSTANT
  []
  [tp0]
    family = MONOMIAL
    order = CONSTANT
  []
  [tp1]
    family = MONOMIAL
    order = CONSTANT
  []
  [tp2]
    family = MONOMIAL
    order = CONSTANT
  []
[]
[AuxKernels]
  [hys_order]
    type = PorousFlowPropertyAux
    variable = hys_order
    property = hysteresis_order
  []
  [tp0]
    type = PorousFlowPropertyAux
    variable = tp0
    property = hysteresis_saturation_turning_point
    hysteresis_turning_point = 0
  []
  [tp1]
    type = PorousFlowPropertyAux
    variable = tp1
    property = hysteresis_saturation_turning_point
    hysteresis_turning_point = 1
  []
  [tp2]
    type = PorousFlowPropertyAux
    variable = tp2
    property = hysteresis_saturation_turning_point
    hysteresis_turning_point = 2
  []
[]
[Functions]
  [sink_strength_fcn]
    type = ParsedFunction
    expression = '-30'
  []
[]
[Postprocessors]
  [sink_strength]
    type = FunctionValuePostprocessor
    function = sink_strength_fcn
    outputs = 'none'
  []
  [saturation]
    type = PointValue
    point = '0 0 0'
    variable = saturation0
  []
  [hys_order]
    type = PointValue
    point = '0 0 0'
    variable = hys_order
  []
  [tp0]
    type = PointValue
    point = '0 0 0'
    variable = tp0
  []
  [tp1]
    type = PointValue
    point = '0 0 0'
    variable = tp1
  []
  [tp2]
    type = PointValue
    point = '0 0 0'
    variable = tp2
  []
[]
[Preconditioning]
  [basic]
    type = SMP
    full = true
  []
[]
[Executioner]
  type = Transient
  solve_type = Newton
  dt = 1
  end_time = 6
  nl_abs_tol = 1E-7
[]
[Outputs]
  [csv]
    type = CSV
  []
[]
(modules/porous_flow/test/tests/sinks/s14.i)
# Apply a PorousFlowPointSourceFromPostprocessor that injects 1kg/s into a 2D model, and PorousFlowOutflowBCs to the outer boundaries to show that the PorousFlowOutflowBCs allow fluid to exit freely at the appropriate rate
[Mesh]
  type = GeneratedMesh
  dim = 2
  nx = 3
  xmin = -1
  xmax = 1
  ny = 2
  ymin = -2
  ymax = 2
[]
[GlobalParams]
  PorousFlowDictator = dictator
  gravity = '0 0 0'
[]
[Variables]
  [pp]
  []
[]
[PorousFlowFullySaturated]
  fp = simple_fluid
  porepressure = pp
[]
[DiracKernels]
  [injection]
    type = PorousFlowPointSourceFromPostprocessor
    mass_flux = 1
    point = '0 0 0'
    variable = pp
  []
[]
[FluidProperties]
  [simple_fluid]
    type = SimpleFluidProperties
    bulk_modulus = 1
  []
[]
[Materials]
  [porosity]
    type = PorousFlowPorosityConst
    porosity = 0.12
  []
  [permeability]
    type = PorousFlowPermeabilityConst
    permeability = '0.4 0 0 0 0.4 0 0 0 0.4'
  []
[]
[BCs]
  [outflow]
    type = PorousFlowOutflowBC
    boundary = 'left right top bottom'
    variable = pp
    save_in = nodal_outflow
  []
[]
[AuxVariables]
  [nodal_outflow]
  []
[]
[Postprocessors]
  [outflow_kg_per_s]
    type = NodalSum
    boundary = 'left right top bottom'
    variable = nodal_outflow
  []
[]
[Preconditioning]
  [andy]
    type = SMP
    full = true
  []
[]
[Executioner]
  type = Transient
  solve_type = Newton
  dt = 3E-4
  end_time = 30E-4
  nl_abs_tol = 1E-9
  nl_rel_tol = 1E-9
[]
[Outputs]
  csv = true
[]
(modules/porous_flow/test/tests/hysteresis/2phasePS_2.i)
# Simple example of a 2-phase situation with hysteretic capillary pressure.  Gas is added to, removed from, and added to the system in order to observe the hysteresis
# All liquid water exists in component 0
# All gas exists in component 1
[Mesh]
  [mesh]
    type = GeneratedMeshGenerator
    dim = 1
  []
[]
[GlobalParams]
  PorousFlowDictator = dictator
[]
[UserObjects]
  [dictator]
    type = PorousFlowDictator
    number_fluid_phases = 2
    number_fluid_components = 2
    porous_flow_vars = 'pp0 sat1'
  []
[]
[Variables]
  [pp0]
  []
  [sat1]
    initial_condition = 0
  []
[]
[Kernels]
  [mass_conservation0]
    type = PorousFlowMassTimeDerivative
    fluid_component = 0
    variable = pp0
  []
  [mass_conservation1]
    type = PorousFlowMassTimeDerivative
    fluid_component = 1
    variable = sat1
  []
[]
[DiracKernels]
  [pump]
    type = PorousFlowPointSourceFromPostprocessor
    mass_flux = flux
    point = '0.5 0 0'
    variable = sat1
  []
[]
[AuxVariables]
  [massfrac_ph0_sp0]
    initial_condition = 1
  []
  [massfrac_ph1_sp0]
    initial_condition = 0
  []
  [sat0]
    family = MONOMIAL
    order = CONSTANT
  []
  [pp1]
    family = MONOMIAL
    order = CONSTANT
  []
  [hys_order]
    family = MONOMIAL
    order = CONSTANT
  []
[]
[AuxKernels]
  [sat0]
    type = PorousFlowPropertyAux
    variable = sat0
    phase = 0
    property = saturation
  []
  [pp1]
    type = PorousFlowPropertyAux
    variable = pp1
    phase = 1
    property = pressure
  []
  [hys_order]
    type = PorousFlowPropertyAux
    variable = hys_order
    property = hysteresis_order
  []
[]
[FluidProperties]
  [simple_fluid] # same properties used for both phases
    type = SimpleFluidProperties
    bulk_modulus = 10 # so pumping does not result in excessive porepressure
  []
[]
[Materials]
  [porosity]
    type = PorousFlowPorosityConst
    porosity = 0.1
  []
  [temperature]
    type = PorousFlowTemperature
    temperature = 20
  []
  [massfrac]
    type = PorousFlowMassFraction
    mass_fraction_vars = 'massfrac_ph0_sp0 massfrac_ph1_sp0'
  []
  [simple_fluid0]
    type = PorousFlowSingleComponentFluid
    fp = simple_fluid
    phase = 0
  []
  [simple_fluid1]
    type = PorousFlowSingleComponentFluid
    fp = simple_fluid
    phase = 1
  []
  [hys_order_material]
    type = PorousFlowHysteresisOrder
  []
  [pc_calculator]
    type = PorousFlow2PhaseHysPS
    alpha_d = 10.0
    alpha_w = 7.0
    n_d = 1.5
    n_w = 1.9
    S_l_min = 0.1
    S_lr = 0.2
    S_gr_max = 0.3
    Pc_max = 12.0
    high_ratio = 0.9
    low_extension_type = quadratic
    high_extension_type = power
    phase0_porepressure = pp0
    phase1_saturation = sat1
  []
[]
[Postprocessors]
  [flux]
    type = FunctionValuePostprocessor
  function = 'if(t <= 14, 10, if(t <= 25, -10, 10))'
  []
  [hys_order]
    type = PointValue
    point = '0 0 0'
    variable = hys_order
  []
  [sat0]
    type = PointValue
    point = '0 0 0'
    variable = sat0
  []
  [sat1]
    type = PointValue
    point = '0 0 0'
    variable = sat1
  []
  [pp0]
    type = PointValue
    point = '0 0 0'
    variable = pp0
  []
  [pp1]
    type = PointValue
    point = '0 0 0'
    variable = pp1
  []
[]
[Preconditioning]
  [smp]
    type = SMP
    full = true
    petsc_options_iname = '-pc_type -pc_factor_shift_type'
    petsc_options_value = ' lu       NONZERO'
  []
[]
[Executioner]
  type = Transient
  solve_type = Newton
  dt = 4
  end_time = 46
  nl_abs_tol = 1E-10
[]
[Outputs]
  csv = true
  sync_times = '13 14 15 24 25 25.5 26 27 28 29'
[]
(modules/porous_flow/test/tests/energy_conservation/heat04_rz.i)
# The sample is a single unit element in RZ coordinates
# A constant velocity is applied to the outer boundary is free to move as a source injects heat and fluid into the system
# There is no fluid flow or heat flow.
# Heat energy conservation is checked.
# Mass conservation is checked
[Mesh]
  type = GeneratedMesh
  dim = 2
  nx = 1
  ny = 1
  xmin = 1
  xmax = 2
  ymin = -0.5
  ymax = 0.5
  coord_type = RZ
[]
[GlobalParams]
  displacements = 'disp_r disp_z'
  PorousFlowDictator = dictator
  block = 0
  biot_coefficient = 0.3
[]
[Variables]
  [disp_r]
  []
  [disp_z]
  []
  [pp]
    initial_condition = 0.1
  []
  [temp]
    initial_condition = 10
  []
[]
[BCs]
  [plane_strain]
    type = DirichletBC
    variable = disp_z
    value = 0
    boundary = 'bottom top'
  []
  [rmin_fixed]
    type = DirichletBC
    variable = disp_r
    value = 0
    boundary = left
  []
  [contract]
    type = FunctionDirichletBC
    variable = disp_r
    function = -0.01*t
    boundary = right
  []
[]
[PorousFlowFullySaturated]
  coupling_type = ThermoHydroMechanical
  porepressure = pp
  temperature = temp
  fp = simple_fluid
[]
[DiracKernels]
  [heat_source]
    type = PorousFlowPointSourceFromPostprocessor
    point = '1.5 0 0'
    variable = temp
    mass_flux = 10
  []
  [fluid_source]
    type = PorousFlowPointSourceFromPostprocessor
    point = '1.5 0 0'
    variable = pp
    mass_flux = 1
  []
[]
[FluidProperties]
  [simple_fluid]
    type = SimpleFluidProperties
    bulk_modulus = 0.5
    density0 = 1
    viscosity = 1
    thermal_expansion = 0
    cv = 1.3
  []
[]
[Materials]
  [elasticity_tensor]
    type = ComputeElasticityTensor
    C_ijkl = '1 1.5'
    # bulk modulus is lambda + 2*mu/3 = 1 + 2*1.5/3 = 2
    fill_method = symmetric_isotropic
  []
  [strain]
    type = ComputeAxisymmetricRZSmallStrain
  []
  [stress]
    type = ComputeLinearElasticStress
  []
  [porosity]
    type = PorousFlowPorosity
    porosity_zero = 0.1
  []
  [rock_heat]
    type = PorousFlowMatrixInternalEnergy
    specific_heat_capacity = 2.2
    density = 0.5
  []
  [permeability]
    type = PorousFlowPermeabilityConst
    permeability = '0.5 0 0   0 0.5 0   0 0 0.5'
  []
  [thermal_cond]
    type = PorousFlowThermalConductivityIdeal
    dry_thermal_conductivity = '1 0 0  0 1 0  0 0 1'
  []
[]
[Postprocessors]
  [p0]
    type = PointValue
    outputs = 'console csv'
    execute_on = 'initial timestep_end'
    point = '1 0 0'
    variable = pp
  []
  [t0]
    type = PointValue
    outputs = 'console csv'
    execute_on = 'initial timestep_end'
    point = '1 0 0'
    variable = temp
  []
  [rdisp]
    type = PointValue
    outputs = 'csv console'
    point = '2 0 0'
    use_displaced_mesh = false
    variable = disp_r
  []
  [fluid_mass]
    type = PorousFlowFluidMass
    fluid_component = 0
    execute_on = 'initial timestep_end'
    outputs = 'console csv'
  []
  [total_heat]
    type = PorousFlowHeatEnergy
    phase = 0
    execute_on = 'initial timestep_end'
    outputs = 'console csv'
  []
  [rock_heat]
    type = PorousFlowHeatEnergy
    execute_on = 'initial timestep_end'
    outputs = 'console csv'
  []
  [fluid_heat]
    type = PorousFlowHeatEnergy
    include_porous_skeleton = false
    phase = 0
    execute_on = 'initial timestep_end'
    outputs = 'console csv'
  []
[]
[Preconditioning]
  [andy]
    type = SMP
    full = true
  []
[]
[Executioner]
  type = Transient
  solve_type = Newton
  dt = 2
  end_time = 10
[]
[Outputs]
  execute_on = 'initial timestep_end'
  [csv]
    type = CSV
  []
[]
(modules/porous_flow/test/tests/hysteresis/hys_order_07.i)
# Test that PorousFlowHysteresisOrder correctly calculates hysteresis order
# Hysteresis order is initialised = 3, with turning points = (0.5, 0.8, 0.66)
# Initial saturation is 0.71
# Water is removed from the system (so order = 3) until saturation = 0.66
# Then, water is removed from the system (so order = 2) until saturation = 0.65
# Then, water is added to the system (so order = 3 with turning point = 0.65) until saturation = 0.8
# Then, water is added to the system (so order = 1) until saturation = 1
# Then, water is added to the system (so order = 0)
[Mesh]
  [mesh]
    type = GeneratedMeshGenerator
    dim = 1
  []
[]
[GlobalParams]
  PorousFlowDictator = dictator
[]
[Variables]
  [pp]
    initial_condition = -9E5
  []
[]
[PorousFlowUnsaturated]
  porepressure = pp
  fp = simple_fluid
[]
[DiracKernels]
  [source_sink_0]
    type = PorousFlowPointSourceFromPostprocessor
    point = '0 0 0'
    mass_flux = sink_strength
    variable = pp
  []
  [source_sink_1]
    type = PorousFlowPointSourceFromPostprocessor
    point = '1 0 0'
    mass_flux = sink_strength
    variable = pp
  []
[]
[FluidProperties]
  [simple_fluid]
    type = SimpleFluidProperties
  []
[]
[Materials]
  [porosity]
    type = PorousFlowPorosityConst
    porosity = 1.0
  []
  [permeability]
    type = PorousFlowPermeabilityConst
    permeability = '0 0 0   0 0 0   0 0 0'
  []
  [hys_order]
    type = PorousFlowHysteresisOrder
    initial_order = 3
    previous_turning_points = '0.6 0.8 0.66'
  []
[]
[AuxVariables]
  [hys_order]
    family = MONOMIAL
    order = CONSTANT
  []
  [tp0]
    family = MONOMIAL
    order = CONSTANT
  []
  [tp1]
    family = MONOMIAL
    order = CONSTANT
  []
  [tp2]
    family = MONOMIAL
    order = CONSTANT
  []
[]
[AuxKernels]
  [hys_order]
    type = PorousFlowPropertyAux
    variable = hys_order
    property = hysteresis_order
  []
  [tp0]
    type = PorousFlowPropertyAux
    variable = tp0
    property = hysteresis_saturation_turning_point
    hysteresis_turning_point = 0
  []
  [tp1]
    type = PorousFlowPropertyAux
    variable = tp1
    property = hysteresis_saturation_turning_point
    hysteresis_turning_point = 1
  []
  [tp2]
    type = PorousFlowPropertyAux
    variable = tp2
    property = hysteresis_saturation_turning_point
    hysteresis_turning_point = 2
  []
[]
[Functions]
  [sink_strength_fcn]
    type = ParsedFunction
    expression = '30 * if(t <= 1, -1, 1)'
  []
[]
[Postprocessors]
  [sink_strength]
    type = FunctionValuePostprocessor
    function = sink_strength_fcn
    outputs = 'none'
  []
  [saturation]
    type = PointValue
    point = '0 0 0'
    variable = saturation0
  []
  [hys_order]
    type = PointValue
    point = '0 0 0'
    variable = hys_order
  []
  [tp0]
    type = PointValue
    point = '0 0 0'
    variable = tp0
  []
  [tp1]
    type = PointValue
    point = '0 0 0'
    variable = tp1
  []
  [tp2]
    type = PointValue
    point = '0 0 0'
    variable = tp2
  []
[]
[Preconditioning]
  [basic]
    type = SMP
    full = true
  []
[]
[Executioner]
  type = Transient
  solve_type = Newton
  dt = 1
  end_time = 9
  nl_abs_tol = 1E-7
[]
[Outputs]
  [csv]
    type = CSV
  []
[]
(modules/porous_flow/test/tests/mass_conservation/mass11.i)
# The sample is a single unit element, with roller BCs on the sides and bottom.
# The top is free to move and fluid is injected at a constant rate of 1kg/s
# There is no fluid flow.
# Fluid mass conservation is checked.
# Under these conditions the fluid mass should increase at 1kg/s
# The porepressure should increase: rho0 * exp(P/bulk) = rho * exp(P0/bulk) + 1*t
# The stress_zz should be exactly biot * P since total stress is zero
[Mesh]
  type = GeneratedMesh
  dim = 3
  nx = 1
  ny = 1
  nz = 1
  xmin = -0.5
  xmax = 0.5
  ymin = -0.5
  ymax = 0.5
  zmin = -0.5
  zmax = 0.5
[]
[GlobalParams]
  displacements = 'disp_x disp_y disp_z'
  PorousFlowDictator = dictator
  block = 0
[]
[Variables]
  [disp_x]
  []
  [disp_y]
  []
  [disp_z]
  []
  [porepressure]
    initial_condition = 0.1
  []
[]
[BCs]
  [confinex]
    type = DirichletBC
    variable = disp_x
    value = 0
    boundary = 'left right'
  []
  [confiney]
    type = DirichletBC
    variable = disp_y
    value = 0
    boundary = 'bottom top'
  []
  [basefixed]
    type = DirichletBC
    variable = disp_z
    value = 0
    boundary = back
  []
[]
[Kernels]
  [grad_stress_x]
    type = StressDivergenceTensors
    variable = disp_x
    component = 0
  []
  [grad_stress_y]
    type = StressDivergenceTensors
    variable = disp_y
    component = 1
  []
  [grad_stress_z]
    type = StressDivergenceTensors
    variable = disp_z
    component = 2
  []
  [poro_x]
    type = PorousFlowEffectiveStressCoupling
    biot_coefficient = 0.3
    variable = disp_x
    component = 0
  []
  [poro_y]
    type = PorousFlowEffectiveStressCoupling
    biot_coefficient = 0.3
    variable = disp_y
    component = 1
  []
  [poro_z]
    type = PorousFlowEffectiveStressCoupling
    biot_coefficient = 0.3
    component = 2
    variable = disp_z
  []
  [poro_vol_exp]
    type = PorousFlowMassVolumetricExpansion
    variable = porepressure
    fluid_component = 0
  []
  [mass0]
    type = PorousFlowMassTimeDerivative
    fluid_component = 0
    variable = porepressure
  []
[]
[DiracKernels]
  [inject]
    type = PorousFlowPointSourceFromPostprocessor
    point = '0 0 0'
    mass_flux = 1.0
    variable = porepressure
  []
[]
[AuxVariables]
  [stress_xx]
    order = CONSTANT
    family = MONOMIAL
  []
  [stress_xy]
    order = CONSTANT
    family = MONOMIAL
  []
  [stress_xz]
    order = CONSTANT
    family = MONOMIAL
  []
  [stress_yy]
    order = CONSTANT
    family = MONOMIAL
  []
  [stress_yz]
    order = CONSTANT
    family = MONOMIAL
  []
  [stress_zz]
    order = CONSTANT
    family = MONOMIAL
  []
[]
[AuxKernels]
  [stress_xx]
    type = RankTwoAux
    rank_two_tensor = stress
    variable = stress_xx
    index_i = 0
    index_j = 0
  []
  [stress_xy]
    type = RankTwoAux
    rank_two_tensor = stress
    variable = stress_xy
    index_i = 0
    index_j = 1
  []
  [stress_xz]
    type = RankTwoAux
    rank_two_tensor = stress
    variable = stress_xz
    index_i = 0
    index_j = 2
  []
  [stress_yy]
    type = RankTwoAux
    rank_two_tensor = stress
    variable = stress_yy
    index_i = 1
    index_j = 1
  []
  [stress_yz]
    type = RankTwoAux
    rank_two_tensor = stress
    variable = stress_yz
    index_i = 1
    index_j = 2
  []
  [stress_zz]
    type = RankTwoAux
    rank_two_tensor = stress
    variable = stress_zz
    index_i = 2
    index_j = 2
  []
[]
[FluidProperties]
  [simple_fluid]
    type = SimpleFluidProperties
    bulk_modulus = 0.5
    density0 = 1
    thermal_expansion = 0
    viscosity = 1
  []
[]
[Materials]
  [temperature]
    type = PorousFlowTemperature
  []
  [elasticity_tensor]
    type = ComputeElasticityTensor
    C_ijkl = '1 1.5'
    # bulk modulus is lambda + 2*mu/3 = 1 + 2*1.5/3 = 2
    fill_method = symmetric_isotropic
  []
  [strain]
    type = ComputeSmallStrain
  []
  [stress]
    type = ComputeLinearElasticStress
  []
  [vol_strain]
    type = PorousFlowVolumetricStrain
  []
  [eff_fluid_pressure]
    type = PorousFlowEffectiveFluidPressure
  []
  [ppss]
    type = PorousFlow1PhaseP
    porepressure = porepressure
    capillary_pressure = pc
  []
  [massfrac]
    type = PorousFlowMassFraction
  []
  [simple_fluid]
    type = PorousFlowSingleComponentFluid
    fp = simple_fluid
    phase = 0
  []
  [porosity]
    type = PorousFlowPorosityConst
    porosity = 0.1
  []
  [permeability]
    type = PorousFlowPermeabilityConst
    permeability = '0.5 0 0   0 0.5 0   0 0 0.5'
  []
[]
[UserObjects]
  [dictator]
    type = PorousFlowDictator
    porous_flow_vars = 'porepressure disp_x disp_y disp_z'
    number_fluid_phases = 1
    number_fluid_components = 1
  []
  [pc]
    type = PorousFlowCapillaryPressureVG
    m = 0.5
    alpha = 1
  []
[]
[Postprocessors]
  [p0]
    type = PointValue
    outputs = 'console csv'
    execute_on = 'initial timestep_end'
    point = '0 0 0'
    variable = porepressure
  []
  [zdisp]
    type = PointValue
    outputs = csv
    point = '0 0 0.5'
    use_displaced_mesh = false
    variable = disp_z
  []
  [stress_xx]
    type = PointValue
    outputs = csv
    point = '0 0 0'
    variable = stress_xx
  []
  [stress_yy]
    type = PointValue
    outputs = csv
    point = '0 0 0'
    variable = stress_yy
  []
  [stress_zz]
    type = PointValue
    outputs = csv
    point = '0 0 0'
    variable = stress_zz
  []
  [fluid_mass]
    type = PorousFlowFluidMass
    fluid_component = 0
    execute_on = 'initial timestep_end'
    outputs = 'console csv'
  []
[]
[Preconditioning]
  [andy]
    type = SMP
    full = true
    petsc_options_iname = '-ksp_type -pc_type -snes_atol -snes_rtol -snes_max_it'
    petsc_options_value = 'bcgs bjacobi 1E-14 1E-8 10000'
  []
[]
[Executioner]
  type = Transient
  solve_type = Newton
  start_time = 0
  end_time = 10
  dt = 2
[]
[Outputs]
  execute_on = 'initial timestep_end'
  [csv]
    type = CSV
  []
[]
(modules/porous_flow/test/tests/hysteresis/hys_order_01.i)
# Test that PorousFlowHysteresisOrder correctly calculates hysteresis order
# Water is removed from the system (so order = 0) until saturation = S0
# Then, water is added to the system (so order = 1) until saturation = S1
# Then, water is removed from the system (so order = 2)
# More water is removed from the system so that the saturation < S0 (so order = 0)
[Mesh]
  [mesh]
    type = GeneratedMeshGenerator
    dim = 1
  []
[]
[GlobalParams]
  PorousFlowDictator = dictator
[]
[Variables]
  [pp]
    initial_condition = 0.0
  []
[]
[PorousFlowUnsaturated]
  porepressure = pp
  fp = simple_fluid
[]
[DiracKernels]
  [source_sink_0]
    type = PorousFlowPointSourceFromPostprocessor
    point = '0 0 0'
    mass_flux = sink_strength
    variable = pp
  []
  [source_sink_1]
    type = PorousFlowPointSourceFromPostprocessor
    point = '1 0 0'
    mass_flux = sink_strength
    variable = pp
  []
[]
[FluidProperties]
  [simple_fluid]
    type = SimpleFluidProperties
  []
[]
[Materials]
  [porosity]
    type = PorousFlowPorosityConst
    porosity = 1.0
  []
  [permeability]
    type = PorousFlowPermeabilityConst
    permeability = '0 0 0   0 0 0   0 0 0'
  []
  [hys_order]
    type = PorousFlowHysteresisOrder
  []
[]
[AuxVariables]
  [hys_order]
    family = MONOMIAL
    order = CONSTANT
  []
  [tp0]
    family = MONOMIAL
    order = CONSTANT
  []
  [tp1]
    family = MONOMIAL
    order = CONSTANT
  []
[]
[AuxKernels]
  [hys_order]
    type = PorousFlowPropertyAux
    variable = hys_order
    property = hysteresis_order
  []
  [tp0]
    type = PorousFlowPropertyAux
    variable = tp0
    property = hysteresis_saturation_turning_point
    hysteresis_turning_point = 0
  []
  [tp1]
    type = PorousFlowPropertyAux
    variable = tp1
    property = hysteresis_saturation_turning_point
    hysteresis_turning_point = 1
  []
[]
[Functions]
  [sink_strength_fcn]
    type = ParsedFunction
    expression = '30 * if(t <= 4, -1, if(t <= 7, 1, -1))'
  []
[]
[Postprocessors]
  [sink_strength]
    type = FunctionValuePostprocessor
    function = sink_strength_fcn
    outputs = 'none'
  []
  [saturation]
    type = PointValue
    point = '0 0 0'
    variable = saturation0
  []
  [hys_order]
    type = PointValue
    point = '0 0 0'
    variable = hys_order
  []
  [tp0]
    type = PointValue
    point = '0 0 0'
    variable = tp0
  []
  [tp1]
    type = PointValue
    point = '0 0 0'
    variable = tp1
  []
[]
[Preconditioning]
  [basic]
    type = SMP
    full = true
  []
[]
[Executioner]
  type = Transient
  solve_type = Newton
  dt = 1
  end_time = 13
  nl_abs_tol = 1E-7
[]
[Outputs]
  [csv]
    type = CSV
    sync_times = '0 1 5 6 7 8 9 10 11 13' # cut out t=12 because numerical roundoff might mean order is not reduced exactly at t=12
    sync_only = true
  []
[]
(modules/porous_flow/test/tests/sinks/s15.i)
# Apply a PorousFlowPointSourceFromPostprocessor that injects 1J/s into a 2D model, and PorousFlowOutflowBCs to the outer boundaries to show that the PorousFlowOutflowBCs allow heat-energy to exit freely at the appropriate rate
[Mesh]
  type = GeneratedMesh
  dim = 2
  nx = 3
  xmin = -1
  xmax = 1
  ny = 2
  ymin = -2
  ymax = 2
[]
[GlobalParams]
  PorousFlowDictator = dictator
  gravity = '0 0 0'
[]
[Variables]
  [pp]
  []
  [T]
    scaling = 1E-7
  []
[]
[PorousFlowFullySaturated]
  fp = simple_fluid
  coupling_type = thermohydro
  porepressure = pp
  temperature = T
[]
[DiracKernels]
  [injection]
    type = PorousFlowPointSourceFromPostprocessor
    mass_flux = 1
    point = '0 0 0'
    variable = T
  []
[]
[FluidProperties]
  [simple_fluid]
    type = SimpleFluidProperties
    bulk_modulus = 1
  []
[]
[Materials]
  [porosity]
    type = PorousFlowPorosityConst
    porosity = 0.12
  []
  [permeability]
    type = PorousFlowPermeabilityConst
    permeability = '0.4 0 0 0 0.4 0 0 0 0.4'
  []
  [matrix]
    type = PorousFlowMatrixInternalEnergy
    density = 0.15
    specific_heat_capacity = 1.5
  []
  [thermal_cond]
    type = PorousFlowThermalConductivityIdeal
    dry_thermal_conductivity = '0.3 0 0 0 0.3 0 0 0 0.3'
  []
[]
[BCs]
  [outflow]
    type = PorousFlowOutflowBC
    boundary = 'left right top bottom'
    flux_type = heat
    variable = T
    save_in = nodal_outflow
  []
[]
[AuxVariables]
  [nodal_outflow]
  []
[]
[Postprocessors]
  [outflow_J_per_s]
    type = NodalSum
    boundary = 'left right top bottom'
    variable = nodal_outflow
  []
[]
[Preconditioning]
  [andy]
    type = SMP
    full = true
  []
[]
[Executioner]
  type = Transient
  solve_type = Newton
  dt = 2E6
  end_time = 2E7
  nl_abs_tol = 1E-14
#  nl_rel_tol = 1E-12
[]
[Outputs]
  csv = true
[]
(modules/porous_flow/test/tests/hysteresis/hys_order_02.i)
# Test that PorousFlowHysteresisOrder correctly calculates hysteresis order
# Water is removed from the system (so order = 0) until saturation = 0.55
# Then, water is added to the system (so order = 1) until saturation = 0.74
# Then, water is removed from the system (so order = 2) until saturation = 0.62
# Then, water is added to the system (so order = 3)
# Then, water is added to the system so that saturation exceeds 0.74, so order = 1
# Then, water is added to the system to saturation becomes 1, so order = 0
[Mesh]
  [mesh]
    type = GeneratedMeshGenerator
    dim = 1
  []
[]
[GlobalParams]
  PorousFlowDictator = dictator
[]
[Variables]
  [pp]
    initial_condition = 0.0
  []
[]
[PorousFlowUnsaturated]
  porepressure = pp
  fp = simple_fluid
[]
[DiracKernels]
  [source_sink_0]
    type = PorousFlowPointSourceFromPostprocessor
    point = '0 0 0'
    mass_flux = sink_strength
    variable = pp
  []
  [source_sink_1]
    type = PorousFlowPointSourceFromPostprocessor
    point = '1 0 0'
    mass_flux = sink_strength
    variable = pp
  []
[]
[FluidProperties]
  [simple_fluid]
    type = SimpleFluidProperties
  []
[]
[Materials]
  [porosity]
    type = PorousFlowPorosityConst
    porosity = 1.0
  []
  [permeability]
    type = PorousFlowPermeabilityConst
    permeability = '0 0 0   0 0 0   0 0 0'
  []
  [hys_order]
    type = PorousFlowHysteresisOrder
  []
[]
[AuxVariables]
  [hys_order]
    family = MONOMIAL
    order = CONSTANT
  []
  [tp0]
    family = MONOMIAL
    order = CONSTANT
  []
  [tp1]
    family = MONOMIAL
    order = CONSTANT
  []
  [tp2]
    family = MONOMIAL
    order = CONSTANT
  []
[]
[AuxKernels]
  [hys_order]
    type = PorousFlowPropertyAux
    variable = hys_order
    property = hysteresis_order
  []
  [tp0]
    type = PorousFlowPropertyAux
    variable = tp0
    property = hysteresis_saturation_turning_point
    hysteresis_turning_point = 0
  []
  [tp1]
    type = PorousFlowPropertyAux
    variable = tp1
    property = hysteresis_saturation_turning_point
    hysteresis_turning_point = 1
  []
  [tp2]
    type = PorousFlowPropertyAux
    variable = tp2
    property = hysteresis_saturation_turning_point
    hysteresis_turning_point = 2
  []
[]
[Functions]
  [sink_strength_fcn]
    type = ParsedFunction
    expression = '30 * if(t <= 7, -1, if(t <= 10, 1, if(t <= 12, -1, 1)))'
  []
[]
[Postprocessors]
  [sink_strength]
    type = FunctionValuePostprocessor
    function = sink_strength_fcn
    outputs = 'none'
  []
  [saturation]
    type = PointValue
    point = '0 0 0'
    variable = saturation0
  []
  [hys_order]
    type = PointValue
    point = '0 0 0'
    variable = hys_order
  []
  [tp0]
    type = PointValue
    point = '0 0 0'
    variable = tp0
  []
  [tp1]
    type = PointValue
    point = '0 0 0'
    variable = tp1
  []
  [tp2]
    type = PointValue
    point = '0 0 0'
    variable = tp2
  []
[]
[Preconditioning]
  [basic]
    type = SMP
    full = true
  []
[]
[Executioner]
  type = Transient
  solve_type = Newton
  dt = 1
  end_time = 21
  nl_abs_tol = 1E-7
[]
[Outputs]
  [csv]
    type = CSV
    sync_times = '0 1 2 9 10 11 12 13 14 15 17 18 19 21' # cut out t=16 and t=20 because numerical roundoff might mean order is not reduced exactly at these times
    sync_only = true
  []
[]
(modules/porous_flow/test/tests/dirackernels/hfrompps.i)
[Mesh]
  type = GeneratedMesh
  dim = 2
  nx = 3
  ny = 3
[]
[GlobalParams]
  PorousFlowDictator = dictator
[]
[Variables]
  [pressure]
  []
  [temperature]
    scaling = 1E-6
  []
[]
[ICs]
  [pressure_ic]
    type = ConstantIC
    variable = pressure
    value = 1e6
  []
  [temperature_ic]
    type = ConstantIC
    variable = temperature
    value = 400
  []
[]
[Kernels]
  [P_time_deriv]
    type = PorousFlowMassTimeDerivative
    fluid_component = 0
    variable = pressure
  []
  [P_flux]
    type = PorousFlowAdvectiveFlux
    fluid_component = 0
    variable = pressure
    gravity = '0 -9.8 0'
  []
  [energy_dot]
    type = PorousFlowEnergyTimeDerivative
    variable = temperature
 []
  [heat_conduction]
    type = PorousFlowHeatConduction
    variable = temperature
  []
  [heat_advection]
    type = PorousFlowHeatAdvection
    variable = temperature
    gravity = '0 -9.8 0'
  []
[]
[UserObjects]
  [dictator]
    type = PorousFlowDictator
    porous_flow_vars = 'pressure temperature'
    number_fluid_phases = 1
    number_fluid_components = 1
  []
  [pc]
    type = PorousFlowCapillaryPressureConst
  []
[]
[Functions]
  [mass_flux_in_fn]
    type = PiecewiseConstant
    direction = left
    xy_data = '
      0    0
      100  0.1
      300  0
      600  0.1
      1400 0
      1500 0.2'
  []
  [T_in_fn]
    type = PiecewiseLinear
    xy_data = '
      0    400
      600  450'
  []
[]
[FluidProperties]
  [simple_fluid]
    type = SimpleFluidProperties
    bulk_modulus = 2e9
    density0 = 1000
    thermal_expansion = 0
  []
[]
[Materials]
  [temperature]
    type = PorousFlowTemperature
    temperature = temperature
  []
  [ppss]
    type = PorousFlow1PhaseP
    porepressure = pressure
    capillary_pressure = pc
  []
  [massfrac]
    type = PorousFlowMassFraction
    at_nodes = true
  []
  [fluid_props]
    type = PorousFlowSingleComponentFluid
    phase = 0
    fp = simple_fluid
  []
  [relperm]
    type = PorousFlowRelativePermeabilityCorey
    n = 1
    phase = 0
  []
  [fp_mat]
    type = FluidPropertiesMaterialPT
    pressure = pressure
    temperature = temperature
    fp = simple_fluid
  []
  [rock_heat]
    type = PorousFlowMatrixInternalEnergy
    specific_heat_capacity = 830.0
    density = 2750
  []
  [thermal_conductivity]
    type = PorousFlowThermalConductivityIdeal
    dry_thermal_conductivity = '2.5 0 0  0 2.5 0  0 0 2.5'
  []
  [permeability]
    type = PorousFlowPermeabilityConst
    permeability = '1.0E-15 0 0  0 1.0E-15 0  0 0 1.0E-14'
  []
  [porosity]
    type = PorousFlowPorosityConst
    porosity = 0.1
  []
[]
[DiracKernels]
  [source]
    type = PorousFlowPointSourceFromPostprocessor
    variable = pressure
    mass_flux = mass_flux_in
    point = '0.5 0.5 0'
  []
  [source_h]
    type = PorousFlowPointEnthalpySourceFromPostprocessor
    variable = temperature
    mass_flux = mass_flux_in
    point = '0.5 0.5 0'
    T_in = T_in
    pressure = pressure
    fp = simple_fluid
  []
[]
[Preconditioning]
  [preferred]
    type = SMP
    full = true
    petsc_options_iname = '-pc_type'
    petsc_options_value = ' lu     '
  []
[]
[Postprocessors]
  [total_mass]
    type = PorousFlowFluidMass
    execute_on = 'initial timestep_end'
  []
  [total_heat]
    type = PorousFlowHeatEnergy
  []
  [mass_flux_in]
    type = FunctionValuePostprocessor
    function = mass_flux_in_fn
    execute_on = 'initial timestep_end'
  []
  [avg_temp]
    type = ElementAverageValue
    variable = temperature
    execute_on = 'initial timestep_end'
  []
  [T_in]
    type = FunctionValuePostprocessor
    function = T_in_fn
    execute_on = 'initial timestep_end'
  []
[]
[Executioner]
  type = Transient
  solve_type = Newton
  nl_abs_tol = 1e-14
  dt = 100
  end_time = 2000
[]
[Outputs]
  csv = true
  execute_on = 'initial timestep_end'
  file_base = hfrompps
[]
(modules/porous_flow/test/tests/hysteresis/hys_order_05.i)
# Test that PorousFlowHysteresisOrder correctly calculates hysteresis order
# Hysteresis order is initialised = 2, with turning points = (0.6, 0.8)
# Initial saturation is 0.71
# Water is removed from the system (so order = 2) until saturation = 0.6
# Then, water is removed from the system (so order = 0) until saturation = 0.58
# Then, water is added to the system (so order = 1 and turning point = 0.58) until saturation = 0.9
# Then, water is removed from the system (so order = 2 and turning point = 0.9)
[Mesh]
  [mesh]
    type = GeneratedMeshGenerator
    dim = 1
  []
[]
[GlobalParams]
  PorousFlowDictator = dictator
[]
[Variables]
  [pp]
    initial_condition = -9E5
  []
[]
[PorousFlowUnsaturated]
  porepressure = pp
  fp = simple_fluid
[]
[DiracKernels]
  [source_sink_0]
    type = PorousFlowPointSourceFromPostprocessor
    point = '0 0 0'
    mass_flux = sink_strength
    variable = pp
  []
  [source_sink_1]
    type = PorousFlowPointSourceFromPostprocessor
    point = '1 0 0'
    mass_flux = sink_strength
    variable = pp
  []
[]
[FluidProperties]
  [simple_fluid]
    type = SimpleFluidProperties
  []
[]
[Materials]
  [porosity]
    type = PorousFlowPorosityConst
    porosity = 1.0
  []
  [permeability]
    type = PorousFlowPermeabilityConst
    permeability = '0 0 0   0 0 0   0 0 0'
  []
  [hys_order]
    type = PorousFlowHysteresisOrder
    initial_order = 2
    previous_turning_points = '0.6 0.8'
  []
[]
[AuxVariables]
  [hys_order]
    family = MONOMIAL
    order = CONSTANT
  []
  [tp0]
    family = MONOMIAL
    order = CONSTANT
  []
  [tp1]
    family = MONOMIAL
    order = CONSTANT
  []
  [tp2]
    family = MONOMIAL
    order = CONSTANT
  []
[]
[AuxKernels]
  [hys_order]
    type = PorousFlowPropertyAux
    variable = hys_order
    property = hysteresis_order
  []
  [tp0]
    type = PorousFlowPropertyAux
    variable = tp0
    property = hysteresis_saturation_turning_point
    hysteresis_turning_point = 0
  []
  [tp1]
    type = PorousFlowPropertyAux
    variable = tp1
    property = hysteresis_saturation_turning_point
    hysteresis_turning_point = 1
  []
  [tp2]
    type = PorousFlowPropertyAux
    variable = tp2
    property = hysteresis_saturation_turning_point
    hysteresis_turning_point = 2
  []
[]
[Functions]
  [sink_strength_fcn]
    type = ParsedFunction
    expression = '30 * if(t <= 2, -1, if(t <= 7, 1, -1))'
  []
[]
[Postprocessors]
  [sink_strength]
    type = FunctionValuePostprocessor
    function = sink_strength_fcn
    outputs = 'none'
  []
  [saturation]
    type = PointValue
    point = '0 0 0'
    variable = saturation0
  []
  [hys_order]
    type = PointValue
    point = '0 0 0'
    variable = hys_order
  []
  [tp0]
    type = PointValue
    point = '0 0 0'
    variable = tp0
  []
  [tp1]
    type = PointValue
    point = '0 0 0'
    variable = tp1
  []
  [tp2]
    type = PointValue
    point = '0 0 0'
    variable = tp2
  []
[]
[Preconditioning]
  [basic]
    type = SMP
    full = true
  []
[]
[Executioner]
  type = Transient
  solve_type = Newton
  dt = 1
  end_time = 10
  nl_abs_tol = 1E-7
[]
[Outputs]
  [csv]
    type = CSV
  []
[]
(modules/porous_flow/test/tests/hysteresis/hys_order_08.i)
# Test that PorousFlowHysteresisOrder correctly calculates hysteresis order
# Hysteresis order is initialised = 3, with turning points = (0.5, 0.8, 0.66)
# Initial saturation is 0.71
# A large amount of water is removed in one timestep so the saturation becomes 0.58 (and order = 0)
# Then, water is added to the system (order = 1, with turning point = 0.58) until saturation = 0.67
# Then, a large amount of water is removed from the system so order becomes 0
[Mesh]
  [mesh]
    type = GeneratedMeshGenerator
    dim = 1
  []
[]
[GlobalParams]
  PorousFlowDictator = dictator
[]
[Variables]
  [pp]
    initial_condition = -9E5
  []
[]
[PorousFlowUnsaturated]
  porepressure = pp
  fp = simple_fluid
[]
[DiracKernels]
  [source_sink_0]
    type = PorousFlowPointSourceFromPostprocessor
    point = '0 0 0'
    mass_flux = sink_strength
    variable = pp
  []
  [source_sink_1]
    type = PorousFlowPointSourceFromPostprocessor
    point = '1 0 0'
    mass_flux = sink_strength
    variable = pp
  []
[]
[FluidProperties]
  [simple_fluid]
    type = SimpleFluidProperties
  []
[]
[Materials]
  [porosity]
    type = PorousFlowPorosityConst
    porosity = 1.0
  []
  [permeability]
    type = PorousFlowPermeabilityConst
    permeability = '0 0 0   0 0 0   0 0 0'
  []
  [hys_order]
    type = PorousFlowHysteresisOrder
    initial_order = 3
    previous_turning_points = '0.6 0.8 0.66'
  []
[]
[AuxVariables]
  [hys_order]
    family = MONOMIAL
    order = CONSTANT
  []
  [tp0]
    family = MONOMIAL
    order = CONSTANT
  []
  [tp1]
    family = MONOMIAL
    order = CONSTANT
  []
  [tp2]
    family = MONOMIAL
    order = CONSTANT
  []
[]
[AuxKernels]
  [hys_order]
    type = PorousFlowPropertyAux
    variable = hys_order
    property = hysteresis_order
  []
  [tp0]
    type = PorousFlowPropertyAux
    variable = tp0
    property = hysteresis_saturation_turning_point
    hysteresis_turning_point = 0
  []
  [tp1]
    type = PorousFlowPropertyAux
    variable = tp1
    property = hysteresis_saturation_turning_point
    hysteresis_turning_point = 1
  []
  [tp2]
    type = PorousFlowPropertyAux
    variable = tp2
    property = hysteresis_saturation_turning_point
    hysteresis_turning_point = 2
  []
[]
[Functions]
  [sink_strength_fcn]
    type = ParsedFunction
  expression = '30 * if(t <= 1, -2, if(t <= 2, 1.5, -2))'
  []
[]
[Postprocessors]
  [sink_strength]
    type = FunctionValuePostprocessor
    function = sink_strength_fcn
    outputs = 'none'
  []
  [saturation]
    type = PointValue
    point = '0 0 0'
    variable = saturation0
  []
  [hys_order]
    type = PointValue
    point = '0 0 0'
    variable = hys_order
  []
  [tp0]
    type = PointValue
    point = '0 0 0'
    variable = tp0
  []
  [tp1]
    type = PointValue
    point = '0 0 0'
    variable = tp1
  []
  [tp2]
    type = PointValue
    point = '0 0 0'
    variable = tp2
  []
[]
[Preconditioning]
  [basic]
    type = SMP
    full = true
  []
[]
[Executioner]
  type = Transient
  solve_type = Newton
  dt = 1
  end_time = 5
  nl_abs_tol = 1E-7
[]
[Outputs]
  [csv]
    type = CSV
  []
[]
(modules/porous_flow/test/tests/hysteresis/1phase.i)
# Simple example of a 1-phase situation with hysteretic capillary pressure.  Water is removed and added to the system in order to observe the hysteresis
[Mesh]
  [mesh]
    type = GeneratedMeshGenerator
    dim = 1
  []
[]
[GlobalParams]
  PorousFlowDictator = dictator
[]
[UserObjects]
  [dictator]
    type = PorousFlowDictator
    number_fluid_phases = 1
    number_fluid_components = 1
    porous_flow_vars = 'pp'
  []
[]
[Variables]
  [pp]
    initial_condition = 0
  []
[]
[Kernels]
  [mass_conservation]
    type = PorousFlowMassTimeDerivative
    variable = pp
  []
[]
[DiracKernels]
  [pump]
    type = PorousFlowPointSourceFromPostprocessor
    mass_flux = flux
    point = '0.5 0 0'
    variable = pp
  []
[]
[AuxVariables]
  [sat]
    family = MONOMIAL
    order = CONSTANT
  []
  [hys_order]
    family = MONOMIAL
    order = CONSTANT
  []
[]
[AuxKernels]
  [sat]
    type = PorousFlowPropertyAux
    variable = sat
    property = saturation
  []
  [hys_order]
    type = PorousFlowPropertyAux
    variable = hys_order
    property = hysteresis_order
  []
[]
[FluidProperties]
  [simple_fluid]
    type = SimpleFluidProperties
  []
[]
[Materials]
  [porosity]
    type = PorousFlowPorosityConst
    porosity = 0.1
  []
  [temperature]
    type = PorousFlowTemperature
    temperature = 20
  []
  [massfrac]
    type = PorousFlowMassFraction
  []
  [simple_fluid]
    type = PorousFlowSingleComponentFluid
    fp = simple_fluid
    phase = 0
  []
  [hys_order_material]
    type = PorousFlowHysteresisOrder
  []
  [pc_calculator]
    type = PorousFlow1PhaseHysP
    alpha_d = 10.0
    alpha_w = 7.0
    n_d = 1.5
    n_w = 1.9
    S_l_min = 0.1
    S_lr = 0.2
    S_gr_max = 0.3
    Pc_max = 12.0
    high_ratio = 0.9
    low_extension_type = quadratic
    high_extension_type = power
    porepressure = pp
  []
[]
[Postprocessors]
  [flux]
    type = FunctionValuePostprocessor
    function = 'if(t <= 9, -10, 10)'
  []
  [hys_order]
    type = PointValue
    point = '0 0 0'
    variable = hys_order
  []
  [sat]
    type = PointValue
    point = '0 0 0'
    variable = sat
  []
  [pp]
    type = PointValue
    point = '0 0 0'
    variable = pp
  []
[]
[Executioner]
  type = Transient
  solve_type = Newton
  dt = 0.5
  end_time = 19
  nl_abs_tol = 1E-10
[]
[Outputs]
  csv = true
[]
(modules/porous_flow/test/tests/hysteresis/1phase_relperm.i)
# Simple example of a 1-phase situation with hysteretic relative permeability.  Water is removed and added to the system in order to observe the hysteresis
[Mesh]
  [mesh]
    type = GeneratedMeshGenerator
    dim = 1
  []
[]
[GlobalParams]
  PorousFlowDictator = dictator
[]
[UserObjects]
  [dictator]
    type = PorousFlowDictator
    number_fluid_phases = 1
    number_fluid_components = 1
    porous_flow_vars = 'pp'
  []
  [pc]
    type = PorousFlowCapillaryPressureVG
    alpha = 10.0
    m = 0.33
  []
[]
[Variables]
  [pp]
    initial_condition = 0
  []
[]
[Kernels]
  [mass_conservation]
    type = PorousFlowMassTimeDerivative
    variable = pp
  []
[]
[DiracKernels]
  [pump]
    type = PorousFlowPointSourceFromPostprocessor
    mass_flux = flux
    point = '0.5 0 0'
    variable = pp
  []
[]
[AuxVariables]
  [sat]
    family = MONOMIAL
    order = CONSTANT
  []
  [relperm]
    family = MONOMIAL
    order = CONSTANT
  []
  [hys_order]
    family = MONOMIAL
    order = CONSTANT
  []
[]
[AuxKernels]
  [sat]
    type = PorousFlowPropertyAux
    variable = sat
    property = saturation
  []
  [relperm]
    type = PorousFlowPropertyAux
    variable = relperm
    property = relperm
    phase = 0
  []
  [hys_order]
    type = PorousFlowPropertyAux
    variable = hys_order
    property = hysteresis_order
  []
[]
[FluidProperties]
  [simple_fluid]
    type = SimpleFluidProperties
  []
[]
[Materials]
  [porosity]
    type = PorousFlowPorosityConst
    porosity = 0.1
  []
  [temperature]
    type = PorousFlowTemperature
    temperature = 20
  []
  [massfrac]
    type = PorousFlowMassFraction
  []
  [simple_fluid]
    type = PorousFlowSingleComponentFluid
    fp = simple_fluid
    phase = 0
  []
  [pc_calculator]
    type = PorousFlow1PhaseP
    capillary_pressure = pc
    porepressure = pp
  []
  [hys_order_material]
    type = PorousFlowHysteresisOrder
  []
  [relperm_material]
    type = PorousFlowHystereticRelativePermeabilityLiquid
    phase = 0
    S_lr = 0.1
    S_gr_max = 0.2
    m = 0.9
    liquid_modification_range = 0.9
  []
[]
[Postprocessors]
  [flux]
    type = FunctionValuePostprocessor
    function = 'if(t <= 5, -10, 10)'
  []
  [hys_order]
    type = PointValue
    point = '0 0 0'
    variable = hys_order
  []
  [sat]
    type = PointValue
    point = '0 0 0'
    variable = sat
  []
  [relperm]
    type = PointValue
    point = '0 0 0'
    variable = relperm
  []
[]
[Executioner]
  type = Transient
  solve_type = Newton
  dt = 0.5
  end_time = 10
  nl_abs_tol = 1E-10
[]
[Outputs]
  csv = true
[]
(modules/porous_flow/test/tests/hysteresis/2phasePP.i)
# Simple example of a 2-phase situation with hysteretic capillary pressure.  Gas is added to and removed from the system in order to observe the hysteresis
# All liquid water exists in component 0
# All gas exists in component 1
[Mesh]
  [mesh]
    type = GeneratedMeshGenerator
    dim = 1
  []
[]
[GlobalParams]
  PorousFlowDictator = dictator
[]
[UserObjects]
  [dictator]
    type = PorousFlowDictator
    number_fluid_phases = 2
    number_fluid_components = 2
    porous_flow_vars = 'pp0 pp1'
  []
[]
[Variables]
  [pp0]
    initial_condition = 0
  []
  [pp1]
    initial_condition = 1E-4
  []
[]
[Kernels]
  [mass_conservation0]
    type = PorousFlowMassTimeDerivative
    fluid_component = 0
    variable = pp0
  []
  [mass_conservation1]
    type = PorousFlowMassTimeDerivative
    fluid_component = 1
    variable = pp1
  []
[]
[DiracKernels]
  [pump]
    type = PorousFlowPointSourceFromPostprocessor
    mass_flux = flux
    point = '0.5 0 0'
    variable = pp1
  []
[]
[AuxVariables]
  [massfrac_ph0_sp0]
    initial_condition = 1
  []
  [massfrac_ph1_sp0]
    initial_condition = 0
  []
  [sat0]
    family = MONOMIAL
    order = CONSTANT
  []
  [sat1]
    family = MONOMIAL
    order = CONSTANT
  []
  [hys_order]
    family = MONOMIAL
    order = CONSTANT
  []
[]
[AuxKernels]
  [sat0]
    type = PorousFlowPropertyAux
    variable = sat0
    phase = 0
    property = saturation
  []
  [sat1]
    type = PorousFlowPropertyAux
    variable = sat1
    phase = 1
    property = saturation
  []
  [hys_order]
    type = PorousFlowPropertyAux
    variable = hys_order
    property = hysteresis_order
  []
[]
[FluidProperties]
  [simple_fluid] # same properties used for both phases
    type = SimpleFluidProperties
    bulk_modulus = 10 # so pumping does not result in excessive porepressure
  []
[]
[Materials]
  [porosity]
    type = PorousFlowPorosityConst
    porosity = 0.1
  []
  [temperature]
    type = PorousFlowTemperature
    temperature = 20
  []
  [massfrac]
    type = PorousFlowMassFraction
    mass_fraction_vars = 'massfrac_ph0_sp0 massfrac_ph1_sp0'
  []
  [simple_fluid0]
    type = PorousFlowSingleComponentFluid
    fp = simple_fluid
    phase = 0
  []
  [simple_fluid1]
    type = PorousFlowSingleComponentFluid
    fp = simple_fluid
    phase = 1
  []
  [hys_order_material]
    type = PorousFlowHysteresisOrder
  []
  [pc_calculator]
    type = PorousFlow2PhaseHysPP
    alpha_d = 10.0
    alpha_w = 7.0
    n_d = 1.5
    n_w = 1.9
    S_l_min = 0.1
    S_lr = 0.2
    S_gr_max = 0.3
    Pc_max = 12.0
    high_ratio = 0.9
    low_extension_type = quadratic
    high_extension_type = power
    phase0_porepressure = pp0
    phase1_porepressure = pp1
  []
[]
[Postprocessors]
  [flux]
    type = FunctionValuePostprocessor
    function = 'if(t <= 9, 10, -10)'
  []
  [hys_order]
    type = PointValue
    point = '0 0 0'
    variable = hys_order
  []
  [sat0]
    type = PointValue
    point = '0 0 0'
    variable = sat0
  []
  [sat1]
    type = PointValue
    point = '0 0 0'
    variable = sat1
  []
  [pp0]
    type = PointValue
    point = '0 0 0'
    variable = pp0
  []
  [pp1]
    type = PointValue
    point = '0 0 0'
    variable = pp1
  []
[]
[Preconditioning]
  [smp]
    type = SMP
    full = true
    petsc_options_iname = '-pc_type -pc_factor_shift_type'
    petsc_options_value = ' lu       NONZERO'
  []
[]
[Executioner]
  type = Transient
  solve_type = Newton
  dt = 0.5
  end_time = 18
  nl_abs_tol = 1E-10
[]
[Outputs]
  csv = true
[]
(modules/porous_flow/test/tests/hysteresis/1phase_relperm_2.i)
# Simple example of a 1-phase situation with hysteretic relative permeability.  Water is removed and added to the system in order to observe the hysteresis
[Mesh]
  [mesh]
    type = GeneratedMeshGenerator
    dim = 1
  []
[]
[GlobalParams]
  PorousFlowDictator = dictator
[]
[UserObjects]
  [dictator]
    type = PorousFlowDictator
    number_fluid_phases = 1
    number_fluid_components = 1
    porous_flow_vars = 'pp'
  []
  [pc]
    type = PorousFlowCapillaryPressureVG
    alpha = 10.0
    m = 0.33
  []
[]
[Variables]
  [pp]
    initial_condition = 0
  []
[]
[Kernels]
  [mass_conservation]
    type = PorousFlowMassTimeDerivative
    variable = pp
  []
[]
[DiracKernels]
  [pump]
    type = PorousFlowPointSourceFromPostprocessor
    mass_flux = flux
    point = '0.5 0 0'
    variable = pp
  []
[]
[AuxVariables]
  [sat]
    family = MONOMIAL
    order = CONSTANT
  []
  [relperm]
    family = MONOMIAL
    order = CONSTANT
  []
  [hys_order]
    family = MONOMIAL
    order = CONSTANT
  []
[]
[AuxKernels]
  [sat]
    type = PorousFlowPropertyAux
    variable = sat
    property = saturation
  []
  [relperm]
    type = PorousFlowPropertyAux
    variable = relperm
    property = relperm
    phase = 0
  []
  [hys_order]
    type = PorousFlowPropertyAux
    variable = hys_order
    property = hysteresis_order
  []
[]
[FluidProperties]
  [simple_fluid]
    type = SimpleFluidProperties
  []
[]
[Materials]
  [porosity]
    type = PorousFlowPorosityConst
    porosity = 0.1
  []
  [temperature]
    type = PorousFlowTemperature
    temperature = 20
  []
  [massfrac]
    type = PorousFlowMassFraction
  []
  [simple_fluid]
    type = PorousFlowSingleComponentFluid
    fp = simple_fluid
    phase = 0
  []
  [pc_calculator]
    type = PorousFlow1PhaseP
    capillary_pressure = pc
    porepressure = pp
  []
  [hys_order_material]
    type = PorousFlowHysteresisOrder
  []
  [relperm_material]
    type = PorousFlowHystereticRelativePermeabilityLiquid
    phase = 0
    S_lr = 0.1
    S_gr_max = 0.2
    m = 0.9
    liquid_modification_range = 0.9
  []
[]
[Postprocessors]
  [flux]
    type = FunctionValuePostprocessor
    function = 'if(t <= 3, -10, if(t <= 5, 10, if(t <= 13, -10, 10)))'
  []
  [hys_order]
    type = PointValue
    point = '0 0 0'
    variable = hys_order
  []
  [sat]
    type = PointValue
    point = '0 0 0'
    variable = sat
  []
  [relperm]
    type = PointValue
    point = '0 0 0'
    variable = relperm
  []
[]
[Executioner]
  type = Transient
  solve_type = Newton
  dt = 3
  end_time = 25
  nl_abs_tol = 1E-10
[]
[Outputs]
  [csv]
    type = CSV
    sync_times = '1 2 2.75 3 4 4.5 5 5.25 6 7.5 9 12 13 13.25 13.5 13.75 14 14.25 15 16 19 22 25'
    sync_only = true
  []
[]
(modules/porous_flow/test/tests/hysteresis/2phasePP_2.i)
# Simple example of a 2-phase situation with hysteretic capillary pressure.  Gas is added to, removed from, and added to the system in order to observe the hysteresis
# All liquid water exists in component 0
# All gas exists in component 1
[Mesh]
  [mesh]
    type = GeneratedMeshGenerator
    dim = 1
  []
[]
[GlobalParams]
  PorousFlowDictator = dictator
[]
[UserObjects]
  [dictator]
    type = PorousFlowDictator
    number_fluid_phases = 2
    number_fluid_components = 2
    porous_flow_vars = 'pp0 pp1'
  []
[]
[Variables]
  [pp0]
    initial_condition = 0
  []
  [pp1]
    initial_condition = 1E-4
  []
[]
[Kernels]
  [mass_conservation0]
    type = PorousFlowMassTimeDerivative
    fluid_component = 0
    variable = pp0
  []
  [mass_conservation1]
    type = PorousFlowMassTimeDerivative
    fluid_component = 1
    variable = pp1
  []
[]
[DiracKernels]
  [pump]
    type = PorousFlowPointSourceFromPostprocessor
    mass_flux = flux
    point = '0.5 0 0'
    variable = pp1
  []
[]
[AuxVariables]
  [massfrac_ph0_sp0]
    initial_condition = 1
  []
  [massfrac_ph1_sp0]
    initial_condition = 0
  []
  [sat0]
    family = MONOMIAL
    order = CONSTANT
  []
  [sat1]
    family = MONOMIAL
    order = CONSTANT
  []
  [hys_order]
    family = MONOMIAL
    order = CONSTANT
  []
[]
[AuxKernels]
  [sat0]
    type = PorousFlowPropertyAux
    variable = sat0
    phase = 0
    property = saturation
  []
  [sat1]
    type = PorousFlowPropertyAux
    variable = sat1
    phase = 1
    property = saturation
  []
  [hys_order]
    type = PorousFlowPropertyAux
    variable = hys_order
    property = hysteresis_order
  []
[]
[FluidProperties]
  [simple_fluid] # same properties used for both phases
    type = SimpleFluidProperties
    bulk_modulus = 10 # so pumping does not result in excessive porepressure
  []
[]
[Materials]
  [porosity]
    type = PorousFlowPorosityConst
    porosity = 0.1
  []
  [temperature]
    type = PorousFlowTemperature
    temperature = 20
  []
  [massfrac]
    type = PorousFlowMassFraction
    mass_fraction_vars = 'massfrac_ph0_sp0 massfrac_ph1_sp0'
  []
  [simple_fluid0]
    type = PorousFlowSingleComponentFluid
    fp = simple_fluid
    phase = 0
  []
  [simple_fluid1]
    type = PorousFlowSingleComponentFluid
    fp = simple_fluid
    phase = 1
  []
  [hys_order_material]
    type = PorousFlowHysteresisOrder
  []
  [pc_calculator]
    type = PorousFlow2PhaseHysPP
    alpha_d = 10.0
    alpha_w = 7.0
    n_d = 1.5
    n_w = 1.9
    S_l_min = 0.1
    S_lr = 0.2
    S_gr_max = 0.3
    Pc_max = 12.0
    high_ratio = 0.9
    low_extension_type = quadratic
    high_extension_type = power
    phase0_porepressure = pp0
    phase1_porepressure = pp1
  []
[]
[Postprocessors]
  [flux]
    type = FunctionValuePostprocessor
  function = 'if(t <= 14, 10, if(t <= 25, -10, 10))'
  []
  [hys_order]
    type = PointValue
    point = '0 0 0'
    variable = hys_order
  []
  [sat0]
    type = PointValue
    point = '0 0 0'
    variable = sat0
  []
  [sat1]
    type = PointValue
    point = '0 0 0'
    variable = sat1
  []
  [pp0]
    type = PointValue
    point = '0 0 0'
    variable = pp0
  []
  [pp1]
    type = PointValue
    point = '0 0 0'
    variable = pp1
  []
[]
[Preconditioning]
  [smp]
    type = SMP
    full = true
    petsc_options_iname = '-pc_type -pc_factor_shift_type'
    petsc_options_value = ' lu       NONZERO'
  []
[]
[Executioner]
  type = Transient
  solve_type = Newton
  dt = 4
  end_time = 46
  nl_abs_tol = 1E-10
[]
[Outputs]
  csv = true
  sync_times = '13 14 15 24 25 25.5 26 27 28 29'
[]