Add sensors and actuators#
.sensor() adds a sensor and the property it observes. .actuator() adds a
command property and the actuator that carries it out. Both work on
equipment, named points, and connection handles.
from plumb import Model, connect
from plumb.qudt import quantity_kinds, units
from plumb.watr import LevelSensor, Pump, Tank, enums
from plumb.watr.drinking_water import ChlorinationStage, RapidSandFiltrationStage
values = enums.values
model = Model("urn:example:instruments#")
with model:
sand_filter = RapidSandFiltrationStage("sand-filter")
chlorination = ChlorinationStage("chlorine-contact")
clearwell = Tank("clearwell")
high_service = Pump("high-service-pump")
connect(sand_filter.filtered_water, chlorination.inlet, name="filtered-water")
connect(chlorination.outlet, clearwell, name="disinfected-water")
finished = connect(clearwell, high_service, name="finished-water")
Add a sensor#
Call .sensor() on what is measured. A turbidimeter on the filter outlet:
turbidimeter = sand_filter.filtered_water.sensor(
"filter-effluent-turbidity",
kind=quantity_kinds.Turbidity,
unit=units.NTU,
)
turbidimeter, turbidimeter.observes
(<TurbidityMeter 'filter-effluent-turbidity'>,
<QuantifiableObservableProperty 'filter-effluent-turbidity-value'>)
The sensor class is the most specific one the package defines for the
quantity kind: TurbidityMeter, FlowSensor, ConcentrationSensor,
ConductivitySensor, TemperatureSensor, or PressureSensor in WaTr. The
observed property is named <name>-value; of_medium, of_substance, and
value go to it.
Measure a connection#
A chlorine analyzer on a connection handle:
analyzer = finished.sensor(
"chlorine-residual",
kind=quantity_kinds.MassConcentration,
unit=units.MilliGM_PER_L,
of_substance=values.Constituent_ChlorineResidual,
)
analyzer.has_observation_location
<ConnectionHandle finished-water: clearwell -> high-service-pump>
Resolution replaces the handle with the concrete pipe.
Choose the sensor class#
A kind with no specific class gives a plain Sensor. Pass sensor_class=:
level = clearwell.sensor(
"clearwell-level",
kind=quantity_kinds.Length,
unit=units.M,
sensor_class=LevelSensor,
)
level
<LevelSensor 'clearwell-level'>
Observe a status#
An enumeration kind gives an EnumeratedObservableProperty instead of a
number:
running = high_service.sensor("high-service-running", kind=values.Binary_OnOff)
running.observes
<EnumeratedObservableProperty 'high-service-running-value'>
Add an actuator#
.actuator() creates a <name>-command property and an Actuator that
actuates the equipment. Both are actuated_by_property the command:
speed = high_service.actuator(
"high-service-speed", kind=quantity_kinds.Frequency, unit=units.HZ
)
start = high_service.actuator(
"high-service-start", kind=values.Binary_OnOff, value=values.OnOff_Off
)
list(high_service.actuated_by_property)
[<QuantifiableActuatableProperty 'high-service-speed-command'>,
<EnumeratedActuatableProperty 'high-service-start-command'>]
A quantity kind gives a QuantifiableActuatableProperty. An enumeration
kind gives an EnumeratedActuatableProperty; value= must be a member.
Resolve and draw#
resolved = model.compile()
analyzer = resolved["chlorine-residual"]
analyzer.has_observation_location is resolved.connection(finished)
True
The observed property takes the pipe’s medium; see Attach quantities:
analyzer.observes.of_medium
<enum Fluid-Water>
from plumb import flowsheet
flowsheet(resolved)
Build a sensor by hand#
.sensor() is shorthand for:
from plumb.watr import TurbidityMeter
turbidity = filtered.quantity(
"filtered-turbidity-value",
0.1,
unit=units.NTU,
kind=quantity_kinds.Turbidity,
capability="observable",
)
TurbidityMeter(
"filtered-turbidity",
has_observation_location=filtered,
observes=turbidity,
)
has_observation_location and has_reference_location accept handles,
ports, and named points. Other relations need concrete objects.