• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

GeoStat-Framework / welltestpy / 10691143420

03 Sep 2024 09:48PM UTC coverage: 76.813% (+0.8%) from 76.01%
10691143420

Pull #35

github

web-flow
Merge 1ebfe99fb into 81a2299af
Pull Request #35: Bump actions/download-artifact from 2 to 4.1.7 in /.github/workflows

1928 of 2510 relevant lines covered (76.81%)

10.59 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

50.8
/src/welltestpy/data/campaignlib.py
1
"""Welltestpy subpackage providing flow datastructures for field-campaigns."""
2
from copy import deepcopy as dcopy
14✔
3

4
from ..tools import plotter
14✔
5
from . import data_io, testslib, varlib
14✔
6

7
__all__ = ["FieldSite", "Campaign"]
14✔
8

9

10
class FieldSite:
14✔
11
    """Class for a field site.
12

13
    This is a class for a field site.
14
    It has a name and a description.
15

16
    Parameters
17
    ----------
18
    name : :class:`str`
19
        Name of the field site.
20
    description : :class:`str`, optional
21
        Description of the field site.
22
        Default: ``"no description"``
23
    coordinates : :class:`Variable`, optional
24
        Coordinates of the field site (lat, lon).
25
        Default: ``None``
26
    """
27

28
    def __init__(self, name, description="Field site", coordinates=None):
14✔
29
        self.name = data_io._formstr(name)
14✔
30
        self.description = str(description)
14✔
31
        self._coordinates = None
14✔
32
        self.coordinates = coordinates
14✔
33

34
    @property
14✔
35
    def info(self):
12✔
36
        """:class:`str`: Info about the field site."""
37
        info = ""
×
38
        info += "----" + "\n"
×
39
        info += "Field-site:   " + str(self.name) + "\n"
×
40
        info += "Description:  " + str(self.description) + "\n"
×
41
        info += "--" + "\n"
×
42
        if self._coordinates is not None:
×
43
            info += self._coordinates.info + "\n"
×
44
        info += "----" + "\n"
×
45
        return info
×
46

47
    @property
14✔
48
    def pos(self):
12✔
49
        """:class:`numpy.ndarray`: Position of the field site."""
50
        if self._coordinates is not None:
×
51
            return self._coordinates.value
×
52
        return None
×
53

54
    @property
14✔
55
    def coordinates(self):
12✔
56
        """:class:`numpy.ndarray`: Coordinates of the field site."""
57
        if self._coordinates is not None:
14✔
58
            return self._coordinates
14✔
59
        return None
×
60

61
    @coordinates.setter
14✔
62
    def coordinates(self, coordinates):
12✔
63
        if coordinates is not None:
14✔
64
            if isinstance(coordinates, varlib.Variable):
14✔
65
                self._coordinates = dcopy(coordinates)
14✔
66
            else:
67
                self._coordinates = varlib.CoordinatesVar(
14✔
68
                    coordinates[0], coordinates[1]
69
                )
70
        else:
71
            self._coordinates = None
×
72

73
    def __repr__(self):
74
        """Representation."""
75
        return self.name
76

77
    def save(self, path="", name=None):
14✔
78
        """Save a field site to file.
79

80
        This writes the field site to a csv file.
81

82
        Parameters
83
        ----------
84
        path : :class:`str`, optional
85
            Path where the variable should be saved. Default: ``""``
86
        name : :class:`str`, optional
87
            Name of the file. If ``None``, the name will be generated by
88
            ``"Field_"+name``. Default: ``None``
89

90
        Notes
91
        -----
92
        The file will get the suffix ``".fds"``.
93
        """
94
        return data_io.save_fieldsite(self, path, name)
14✔
95

96

97
class Campaign:
14✔
98
    """Class for a well based campaign.
99

100
    This is a class for a well based test campaign on a field site.
101
    It has a name, a description and a timeframe.
102

103
    Parameters
104
    ----------
105
    name : :class:`str`
106
        Name of the campaign.
107
    fieldsite : :class:`str` or :class:`Variable`, optional
108
        The field site.
109
        Default: ``"Fieldsite"``
110
    wells : :class:`dict`, optional
111
        The wells within the field site. Keys are the well names and values
112
        are an instance of :class:`Well`.
113
        Default: ``None``
114
    wells : :class:`dict`, optional
115
        The tests within the campaign. Keys are the test names and values
116
        are an instance of :class:`Test`.
117
        Default: ``None``
118
    timeframe : :class:`str`, optional
119
        Timeframe of the campaign.
120
        Default: ``None``
121
    description : :class:`str`, optional
122
        Description of the field site.
123
        Default: ``"Welltest campaign"``
124
    """
125

126
    def __init__(
14✔
127
        self,
128
        name,
129
        fieldsite="Fieldsite",
130
        wells=None,
131
        tests=None,
132
        timeframe=None,
133
        description="Welltest campaign",
134
    ):
135
        self.name = data_io._formstr(name)
14✔
136
        self.description = str(description)
14✔
137
        self._fieldsite = None
14✔
138
        self.fieldsite = fieldsite
14✔
139
        self.__wells = {}
14✔
140
        self.wells = wells
14✔
141
        self.__tests = {}
14✔
142
        self.tests = tests
14✔
143
        self.timeframe = str(timeframe)
14✔
144

145
    @property
14✔
146
    def fieldsite(self):
12✔
147
        """:class:`FieldSite`: Field site where the campaign was realised."""
148
        return self._fieldsite
14✔
149

150
    @fieldsite.setter
14✔
151
    def fieldsite(self, fieldsite):
12✔
152
        if fieldsite is not None:
14✔
153
            if isinstance(fieldsite, FieldSite):
14✔
154
                self._fieldsite = dcopy(fieldsite)
14✔
155
            else:
156
                self._fieldsite = FieldSite(str(fieldsite))
×
157
        else:
158
            self._fieldsite = None
×
159

160
    @property
14✔
161
    def wells(self):
12✔
162
        """:class:`dict`: Wells within the campaign."""
163
        return self.__wells
14✔
164

165
    @wells.setter
14✔
166
    def wells(self, wells):
12✔
167
        if wells is not None:
14✔
168
            if isinstance(wells, dict):
14✔
169
                for k in wells.keys():
14✔
170
                    if not isinstance(wells[k], varlib.Well):
14✔
171
                        raise ValueError(
×
172
                            "Campaign: some 'wells' are not of " + "type Well"
173
                        )
174
                    if not k == wells[k].name:
14✔
175
                        raise ValueError(
×
176
                            "Campaign: 'well'-keys should be "
177
                            + "the Well name"
178
                        )
179
                self.__wells = dcopy(wells)
14✔
180
            elif isinstance(wells, (list, tuple)):
×
181
                for wel in wells:
×
182
                    if not isinstance(wel, varlib.Well):
×
183
                        raise ValueError(
×
184
                            "Campaign: some 'wells' " + "are not of type u"
185
                        )
186
                self.__wells = {}
×
187
                for wel in wells:
×
188
                    self.__wells[wel.name] = dcopy(wel)
×
189
            else:
190
                raise ValueError(
×
191
                    "Campaign: 'wells' should be given "
192
                    + "as dictionary or list"
193
                )
194
        else:
195
            self.__wells = {}
14✔
196
        self.__updatewells()
14✔
197

198
    def add_well(
14✔
199
        self, name, radius, coordinates, welldepth=1.0, aquiferdepth=None
200
    ):
201
        """Add a single well to the campaign.
202

203
        Parameters
204
        ----------
205
        name : :class:`str`
206
            Name of the Variable.
207
        radius : :class:`Variable` or :class:`float`
208
            Value of the Variable.
209
        coordinates : :class:`Variable` or :class:`numpy.ndarray`
210
            Value of the Variable.
211
        welldepth : :class:`Variable` or :class:`float`, optional
212
            Depth of the well. Default: 1.0
213
        aquiferdepth : :class:`Variable` or :class:`float`, optional
214
            Depth of the aquifer at the well. Default: ``"None"``
215
        """
216
        well = varlib.Well(name, radius, coordinates, welldepth, aquiferdepth)
14✔
217
        self.addwells(well)
14✔
218

219
    def addwells(self, wells):
14✔
220
        """Add some specified wells.
221

222
        This will add wells to the campaign.
223

224
        Parameters
225
        ----------
226
        wells : :class:`dict`
227
            Wells to be added.
228
        """
229
        if isinstance(wells, dict):
14✔
230
            for k in wells.keys():
×
231
                if not isinstance(wells[k], varlib.Well):
×
232
                    raise ValueError(
×
233
                        "Campaign_addwells: some 'wells' "
234
                        + "are not of type Well"
235
                    )
236
                if k in tuple(self.__wells.keys()):
×
237
                    raise ValueError(
×
238
                        "Campaign_addwells: some 'wells' "
239
                        + "are already present"
240
                    )
241
                if not k == wells[k].name:
×
242
                    raise ValueError(
×
243
                        "Campaign_addwells: 'well'-keys "
244
                        + "should be the Well name"
245
                    )
246
            for k in wells.keys():
×
247
                self.__wells[k] = dcopy(wells[k])
×
248
        elif isinstance(wells, (list, tuple)):
14✔
249
            for wel in wells:
×
250
                if not isinstance(wel, varlib.Well):
×
251
                    raise ValueError(
×
252
                        "Campaign_addwells: some 'wells' "
253
                        + "are not of type Well"
254
                    )
255
                if wel.name in tuple(self.__wells.keys()):
×
256
                    raise ValueError(
×
257
                        "Campaign_addwells: some 'wells' "
258
                        + "are already present"
259
                    )
260
            for wel in wells:
×
261
                self.__wells[wel.name] = dcopy(wel)
×
262
        elif isinstance(wells, varlib.Well):
14✔
263
            self.__wells[wells.name] = dcopy(wells)
14✔
264
        else:
265
            raise ValueError(
×
266
                "Campaign_addwells: 'wells' should be "
267
                + "given as dictionary, list or single 'Well'"
268
            )
269

270
    def delwells(self, wells):
14✔
271
        """Delete some specified wells.
272

273
        This will delete wells from the campaign. You can give a
274
        list of wells or a single well by name.
275

276
        Parameters
277
        ----------
278
        wells : :class:`list` of :class:`str` or :class:`str`
279
            Wells to be deleted.
280
        """
281
        if isinstance(wells, (list, tuple)):
×
282
            for wel in wells:
×
283
                if wel in tuple(self.__wells.keys()):
×
284
                    del self.__wells[wel]
×
285
        else:
286
            if wells in tuple(self.__wells.keys()):
×
287
                del self.__wells[wells]
×
288

289
    @property
14✔
290
    def tests(self):
12✔
291
        """:class:`dict`: Tests within the campaign."""
292
        return self.__tests
14✔
293

294
    @tests.setter
14✔
295
    def tests(self, tests):
12✔
296
        if tests is not None:
14✔
297
            if isinstance(tests, dict):
14✔
298
                for k in tests.keys():
14✔
299
                    if not isinstance(tests[k], testslib.Test):
14✔
300
                        raise ValueError(
×
301
                            "Campaign: 'tests' are not of " + "type Test"
302
                        )
303
                    if not k == tests[k].name:
14✔
304
                        raise ValueError(
×
305
                            "Campaign: 'tests'-keys "
306
                            + "should be the Test name"
307
                        )
308
                self.__tests = dcopy(tests)
14✔
309
            elif isinstance(tests, (list, tuple)):
×
310
                for tes in tests:
×
311
                    if not isinstance(tes, testslib.Test):
×
312
                        raise ValueError(
×
313
                            "Campaign: some 'tests' are not of " + "type Test"
314
                        )
315
                self.__tests = {}
×
316
                for tes in tests:
×
317
                    self.__tests[tes.name] = dcopy(tes)
×
318
            elif isinstance(tests, testslib.Test):
×
319
                self.__tests[tests.name] = dcopy(tests)
×
320
            else:
321
                raise ValueError(
×
322
                    "Campaign: 'tests' should be given "
323
                    + "as dictionary, list or 'Test'"
324
                )
325
        else:
326
            self.__tests = {}
14✔
327

328
    def addtests(self, tests):
14✔
329
        """Add some specified tests.
330

331
        This will add tests to the campaign.
332

333
        Parameters
334
        ----------
335
        tests : :class:`dict`
336
            Tests to be added.
337
        """
338
        if isinstance(tests, dict):
14✔
339
            for k in tests.keys():
×
340
                if not isinstance(tests[k], testslib.Test):
×
341
                    raise ValueError(
×
342
                        "Campaign_addtests: some 'tests' "
343
                        + "are not of type Test"
344
                    )
345
                if k in tuple(self.__tests.keys()):
×
346
                    raise ValueError(
×
347
                        "Campaign_addtests: some 'tests' "
348
                        + "are already present"
349
                    )
350
                if not k == tests[k].name:
×
351
                    raise ValueError(
×
352
                        "Campaign_addtests: 'tests'-keys "
353
                        + "should be the Test name"
354
                    )
355
            for k in tests.keys():
×
356
                self.__tests[k] = dcopy(tests[k])
×
357
        elif isinstance(tests, (list, tuple)):
14✔
358
            for tes in tests:
×
359
                if not isinstance(tes, testslib.Test):
×
360
                    raise ValueError(
×
361
                        "Campaign_addtests: some 'tests' "
362
                        + "are not of type Test"
363
                    )
364
                if tes.name in tuple(self.__tests.keys()):
×
365
                    raise ValueError(
×
366
                        "Campaign_addtests: some 'tests' "
367
                        + "are already present"
368
                    )
369
            for tes in tests:
×
370
                self.__tests[tes.name] = dcopy(tes)
×
371
        elif isinstance(tests, testslib.Test):
14✔
372
            if tests.name in tuple(self.__tests.keys()):
14✔
373
                raise ValueError("Campaign.addtests: 'test' already present")
×
374
            self.__tests[tests.name] = dcopy(tests)
14✔
375
        else:
376
            raise ValueError(
×
377
                "Campaign_addtests: 'tests' should be "
378
                + "given as dictionary, list or single 'Test'"
379
            )
380

381
    def deltests(self, tests):
14✔
382
        """Delete some specified tests.
383

384
        This will delete tests from the campaign. You can give a
385
        list of tests or a single test by name.
386

387
        Parameters
388
        ----------
389
        tests : :class:`list` of :class:`str` or :class:`str`
390
            Tests to be deleted.
391
        """
392
        if isinstance(tests, (list, tuple)):
×
393
            for tes in tests:
×
394
                if tes in tuple(self.__tests.keys()):
×
395
                    del self.__tests[tes]
×
396
        else:
397
            if tests in tuple(self.__tests.keys()):
×
398
                del self.__tests[tests]
×
399

400
    def __updatewells(self):
14✔
401
        pass
14✔
402

403
    def plot(self, select_tests=None, **kwargs):
14✔
404
        """Generate a plot of the tests within the campaign.
405

406
        This will plot an overview of the tests within the campaign.
407

408
        Parameters
409
        ----------
410
        select_tests : :class:`list`, optional
411
            Tests that should be plotted. If None, all will be displayed.
412
            Default: ``None``
413
        **kwargs
414
            Keyword-arguments forwarded to :py:func:`~welltestpy.tools.campaign_plot`
415
        """
416
        return plotter.campaign_plot(self, select_tests, **kwargs)
14✔
417

418
    def plot_wells(self, **kwargs):
14✔
419
        """Generate a plot of the wells within the campaign.
420

421
        This will plot an overview of the wells within the campaign.
422

423
        Parameters
424
        ----------
425
        **kwargs
426
            Keyword-arguments forwarded to :py:func:`~welltestpy.tools.campaign_well_plot`.
427
        """
428
        return plotter.campaign_well_plot(self, **kwargs)
14✔
429

430
    def diagnostic_plot(self, pumping_test, observation_well, **kwargs):
14✔
431
        """Generate a diagnostic plot.
432

433
        Parameters
434
        ----------
435
        pumping_test : :class:`str`
436
            The pumping well that is saved in the campaign.
437

438
        observation_well : :class:`str`
439
            Observation point to make the diagnostic plot.
440

441
        **kwargs
442
            Keyword-arguments forwarded to :py:func:`~welltestpy.tools.campaign_well_plot`.
443
        """
444
        # check if this is a pumping test
445
        if pumping_test in self.tests:
×
446
            if not isinstance(self.tests[pumping_test], testslib.PumpingTest):
×
447
                raise ValueError(
×
448
                    f"diagnostic_plot: test '{pumping_test}' is not of instance PumpingTest!"
449
                )
450
            # check if the well is present
451
            if observation_well in self.wells:
×
452
                return self.tests[pumping_test].diagnostic_plot(
×
453
                    observation_well=observation_well, **kwargs
454
                )
455
            else:
456
                raise ValueError(
×
457
                    f"diagnostic_plot: well '{observation_well}' could not be found!"
458
                )
459
        else:
460
            raise ValueError(
×
461
                f"diagnostic_plot: test '{pumping_test}' could not be found!"
462
            )
463

464
    def __repr__(self):
465
        """Representation."""
466
        return (
467
            f"Campaign '{self.name}' at '{self.fieldsite}' with "
468
            f"{len(self.wells)} wells and "
469
            f"{len(self.tests)} tests"
470
        )
471

472
    def save(self, path="", name=None):
14✔
473
        """Save the campaign to file.
474

475
        This writes the campaign to a csv file.
476

477
        Parameters
478
        ----------
479
        path : :class:`str`, optional
480
            Path where the variable should be saved. Default: ``""``
481
        name : :class:`str`, optional
482
            Name of the file. If ``None``, the name will be generated by
483
            ``"Cmp_"+name``. Default: ``None``
484

485
        Notes
486
        -----
487
        The file will get the suffix ``".cmp"``.
488
        """
489
        return data_io.save_campaign(self, path, name)
14✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc