-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #72 from LDAR-Sim/FEAT_external_sensor_addition
<Feat> external sensor addition
- Loading branch information
Showing
11 changed files
with
496 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
""" | ||
An alternative sensor, specifically built to replicate the probability of detection | ||
curves provided by a METEC report, which does not factor in wind speeds | ||
""" | ||
# ------------------------------------------------------------------------------ | ||
# Program: The LDAR Simulator (LDAR-Sim) | ||
# File: methods.deployment.OGI_camera_zim | ||
# Purpose: OGI company specific deployment classes and methods based on zimmerle (2020) | ||
# | ||
# Copyright (C) 2018-2021 Intelligent Methane Monitoring and Management System (IM3S) Group | ||
# | ||
# This program is free software: you can redistribute it and/or modify | ||
# it under the terms of the MIT License as published | ||
# by the Free Software Foundation, version 3. | ||
|
||
# This program is distributed in the hope that it will be useful, | ||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
# MIT License for more details. | ||
|
||
# You should have received a copy of the MIT License | ||
# along with this program. If not, see <https://opensource.org/licenses/MIT>. | ||
# | ||
# ------------------------------------------------------------------------------ | ||
import numpy as np | ||
from utils.attribution import update_tag | ||
from methods.funcs import measured_rate | ||
|
||
|
||
def detect_emissions(self, site, covered_leaks, covered_equipment_rates, covered_site_rate, | ||
site_rate, venting, equipment_rates): # pylint: disable=unused-argument | ||
""" | ||
An alternative sensor, specifically built to replicate the probability of detection | ||
curves provided by a METEC report, which does not factor in wind speeds | ||
Utilizes 3 values set as the MDL: | ||
mdl = [a, b, c] | ||
where | ||
a and b : utilize for the PoD curve variables | ||
c : represents the floor/minimum cutoff value of the leak rates that the sensor can detect | ||
PoD = 1 / (1 + e ^ (a - b * r )) | ||
a = first MDL value | ||
b = second MDL value | ||
r = emission rate | ||
Args: | ||
site (site obj): Site in which crew is working at | ||
covered_leaks (list): list of leak objects that can be detected by the crew | ||
covered_equipment_rates (list): list of equipment leak rates that can be | ||
detected by the crew | ||
covered_site_rate (float): total site emissions from leaks that are observable | ||
from a crew | ||
site_rate (float): total site emissions from leaks all leaks at site | ||
venting (float): total site emissions from venting | ||
equipment_rates (list): list of equipment leak rates for each equipment group | ||
Returns: | ||
site report (dict): | ||
site (site obj): same as input | ||
leaks_present (list): same as covered leaks input | ||
site_true_rate (float): same as site_rate | ||
site_measured_rate (float): total emis from all leaks measured | ||
equip_measured_rates (list): total of all leaks measured for each equip group | ||
venting (float): same as input | ||
found_leak (boolean): Did the crew find at least one leak at the site | ||
""" | ||
|
||
missed_leaks_str = '{}_missed_leaks'.format(self.config['label']) | ||
equip_measured_rates = [] | ||
site_measured_rate = 0 | ||
found_leak = False | ||
n_leaks = len(covered_leaks) | ||
mdl = self.config['sensor']['MDL'] | ||
|
||
if self.config["measurement_scale"] == "site": | ||
# factor of 3.6 converts g/s to kg/h | ||
rate = covered_site_rate * 3.6 | ||
prob_detect = 1/(1+np.exp(mdl[0]-mdl[1]*rate)) | ||
if prob_detect >= 1: | ||
prob_detect = 1 | ||
if rate < (mdl[2]*3.6): | ||
site[missed_leaks_str] += n_leaks | ||
self.timeseries[missed_leaks_str][self.state['t'].current_timestep] += n_leaks | ||
elif np.random.binomial(1, prob_detect): | ||
found_leak = True | ||
site_measured_rate = measured_rate( | ||
covered_site_rate, self.config['sensor']['QE']) | ||
else: | ||
site[missed_leaks_str] += n_leaks | ||
self.timeseries[missed_leaks_str][self.state['t'].current_timestep] += n_leaks | ||
elif self.config["measurement_scale"] == "equipment": | ||
for rate in covered_equipment_rates: | ||
m_rate = measured_rate(rate, self.config['sensor']['QE']) | ||
rate = m_rate * 3.6 | ||
prob_detect = 1 / \ | ||
(1+np.exp(mdl[0]-mdl[1]*rate)) | ||
if prob_detect >= 1: | ||
prob_detect = 1 | ||
if rate < (mdl[2]*3.6): | ||
m_rate = 0 | ||
elif np.random.binomial(1, prob_detect): | ||
found_leak = True | ||
else: | ||
m_rate = 0 | ||
equip_measured_rates.append(m_rate) | ||
site_measured_rate += m_rate | ||
if not found_leak: | ||
site[missed_leaks_str] += n_leaks | ||
self.timeseries[missed_leaks_str][self.state['t'].current_timestep] += n_leaks | ||
elif self.config['measurement_scale'] == 'component': | ||
for leak in covered_leaks: | ||
rate = leak['rate'] * 3.6 | ||
prob_detect = 1 / \ | ||
(1+np.exp(mdl[0]-mdl[1]*rate)) | ||
if prob_detect >= 1: | ||
prob_detect = 1 | ||
if rate < (mdl[2]*3.6): | ||
site[missed_leaks_str] += 1 | ||
self.timeseries[missed_leaks_str][self.state['t'].current_timestep] += 1 | ||
elif np.random.binomial(1, prob_detect): | ||
found_leak = True | ||
meas_rate = measured_rate( | ||
leak['rate'], self.config['sensor']['QE']) | ||
is_new_leak = update_tag( | ||
leak, | ||
meas_rate, | ||
site, | ||
self.timeseries, | ||
self.state['t'], | ||
self.config['label'], | ||
self.id, | ||
self.parameters | ||
) | ||
if is_new_leak: | ||
site_measured_rate += meas_rate | ||
else: | ||
site[missed_leaks_str] += 1 | ||
self.timeseries[missed_leaks_str][self.state['t'].current_timestep] += 1 | ||
site_dict = { | ||
'site': site, | ||
'leaks_present': covered_leaks, | ||
'site_true_rate': site_rate, | ||
'site_measured_rate': site_measured_rate, | ||
'equip_measured_rates': equip_measured_rates, | ||
'vent_rate': venting, | ||
'found_leak': found_leak, | ||
} | ||
return site_dict |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
# External Sensor Coding Practices | ||
|
||
This document provides coding guidelines and best practices for developing external sensors/plugins for use with LDAR-Sim. Following these practices will ensure consistency, readability, and maintainability of the codebase. | ||
|
||
## Table of Contents | ||
|
||
- [External Sensor Coding Practices](#external-sensor-coding-practices) | ||
- [Table of Contents](#table-of-contents) | ||
- [General Guidelines](#general-guidelines) | ||
- [File Structure](#file-structure) | ||
- [Naming Conventions](#naming-conventions) | ||
- [Code Formatting](#code-formatting) | ||
- [Documentation](#documentation) | ||
- [Error Handling](#error-handling) | ||
- [Testing](#testing) | ||
- [Detect Emissions Function Requirement](#detect-emissions-function-requirement) | ||
|
||
## General Guidelines | ||
|
||
- Keep the code modular, well-organized, and maintainable. | ||
- Follow the [Python PEP 8 Style Guide](https://www.python.org/dev/peps/pep-0008/) for code style and formatting. | ||
- If possible use the autopep8 auto formatting | ||
- Write code that is concise, readable, and self-explanatory. | ||
- Avoid unnecessary code duplication; favor code reuse. | ||
|
||
## File Structure | ||
|
||
- Organize your plugin files such that they are located in the `external_sensors` folder | ||
- Each sensor should be one file | ||
- Consider using a package structure for larger plugins. | ||
|
||
## Naming Conventions | ||
|
||
- Use descriptive and meaningful names for variables, functions, classes, and modules. | ||
- Follow the Python naming conventions: use lowercase with underscores for variables and functions (`my_variable`, `my_function`), and use CamelCase for classes (`MyClass`). | ||
- Avoid single-character variable names except for simple loop counters. | ||
|
||
## Code Formatting | ||
|
||
- Use consistent and readable code formatting. | ||
- Keep lines within a reasonable length (80-120 characters). | ||
- Use blank lines and proper spacing to enhance readability. | ||
- Follow appropriate naming conventions for constants and global variables. | ||
- If possible use Black as a formatter. | ||
|
||
## Documentation | ||
|
||
- Provide clear and concise comments and docstrings. See [Detect Emissions Function Requirement](#detect-emissions-function-requirement) for more details. | ||
- Include a high-level overview of the plugin's purpose and functionality. | ||
- Document public interfaces, classes, and functions. | ||
- Add inline comments where necessary to explain complex code logic. | ||
- Include code examples or usage instructions when necessary. | ||
|
||
- Within the docstring, be sure to include the probability of detection curve that the sensor should be replicating. Additionally, add in additional values that the function requires as inputs. For example: | ||
|
||
```python | ||
|
||
def detect_emissions(self, site, covered_leaks, covered_equipment_rates, covered_site_rate, | ||
site_rate, venting, equipment_rates): | ||
""" | ||
An alternative sensor | ||
Utilizes 3 values set as the MDL: | ||
mdl = [a, b, c] | ||
where | ||
a and b are utilize for the POD curve variables | ||
c represents the floor/minimum cutoff value of the leak rates | ||
""" | ||
``` | ||
|
||
## Error Handling | ||
|
||
- Use proper error handling techniques to handle exceptions. | ||
- Catch specific exceptions instead of using broad `except` clauses. | ||
- Log or report errors appropriately to aid troubleshooting and debugging. | ||
- Handle exceptions gracefully to avoid crashes or unexpected behavior. | ||
|
||
## Testing | ||
|
||
- Write unit tests to verify the correctness of your plugin. | ||
- Test all major functionalities and edge cases. | ||
- Use test frameworks like `unittest` or `pytest`. | ||
- Ensure the tests are easily runnable and provide clear output. | ||
|
||
## Detect Emissions Function Requirement | ||
|
||
The external sensor function should have the following signature and functionality: | ||
|
||
```python | ||
def detect_emissions(site, covered_leaks, covered_equipment_rates, covered_site_rate, | ||
site_rate, venting, equipment_rates): | ||
""" | ||
Perform sensor measurements and generate a site report. | ||
Args: | ||
site (site obj): Site in which crew is working at | ||
covered_leaks (list): List of leak objects that can be detected by the crew | ||
covered_equipment_rates (list): List of equipment leak rates that can be | ||
detected by the crew | ||
covered_site_rate (float): Total site emissions from leaks that are observable | ||
from a crew | ||
site_rate (float): Total site emissions from all leaks at the site | ||
venting (float): Total site emissions from venting | ||
equipment_rates (list): List of equipment leak rates for each equipment group | ||
Returns: | ||
site_report (dict): | ||
'site' (site obj): Same as input | ||
'leaks_present' (list): Same as covered_leaks input | ||
'site_true_rate' (float): Same as site_rate | ||
'site_measured_rate' (float): Total emissions from all leaks measured | ||
'equip_measured_rates' (list): Total emissions from all leaks measured for each equipment group | ||
'venting' (float): Same as input | ||
'found_leak' (boolean): Indicates if the crew found at least one leak at the site | ||
""" | ||
# Implement the sensor function logic here | ||
# ... | ||
# Return the site report | ||
``` | ||
|
||
**Note:** Rates provided by LDAR-Sim are in g/s, however resulting rate should be in kg/hr. |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
parameter_level: method | ||
version: '2.0' | ||
label: aircraft_ex_sensor | ||
deployment_type: mobile | ||
measurement_scale: equipment | ||
is_follow_up: False | ||
sensor: | ||
mod_loc: METEC_NO_WIND | ||
MDL: [4,5,0] | ||
cost: | ||
per_site: 200 | ||
coverage: | ||
spatial: 0.9 | ||
temporal: 0.9 | ||
t_bw_sites: | ||
vals: [5] | ||
RS: 4 | ||
time: 1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
program_name: P_aircraft_ex_sensor | ||
parameter_level: program | ||
version: '2.0' | ||
method_labels: | ||
- aircraft_ex_sensor | ||
- OGI_FU |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
Oops, something went wrong.