def METHOD_NAME(self, bytes_per_sigop, num_sigops):
    sigop_equivalent_vsize = ceil(num_sigops * bytes_per_sigop / WITNESS_SCALE_FACTOR)
    self.log.info(f"- {num_sigops} sigops (equivalent size of {sigop_equivalent_vsize} vbytes)")
    # create a template tx with the specified sigop cost in the witness script
    # (note that the sigops count even though being in a branch that's not executed)
    num_multisigops = num_sigops // 20
    num_singlesigops = num_sigops % 20
    witness_script = CScript(
        [OP_FALSE, OP_IF] +
        [OP_CHECKMULTISIG]*num_multisigops +
        [OP_CHECKSIG]*num_singlesigops +
        [OP_ENDIF, OP_TRUE]
    )
    # use a 256-byte data-push as lower bound in the output script, in order
    # to avoid having to compensate for tx size changes caused by varying
    # length serialization sizes (both for scriptPubKey and data-push lengths)
    tx = self.create_p2wsh_spending_tx(witness_script, CScript([OP_RETURN, b'X'*256]))
    # bump the tx to reach the sigop-limit equivalent size by padding the datacarrier output
    assert_greater_than_or_equal(sigop_equivalent_vsize, tx.get_vsize())
    vsize_to_pad = sigop_equivalent_vsize - tx.get_vsize()
    tx.vout[0].scriptPubKey = CScript([OP_RETURN, b'X'*(256+vsize_to_pad)])
    assert_equal(sigop_equivalent_vsize, tx.get_vsize())
    res = self.nodes[0].testmempoolaccept([tx.serialize().hex()])[0]
    assert_equal(res['allowed'], True)
    assert_equal(res['vsize'], sigop_equivalent_vsize)
    # increase the tx's vsize to be right above the sigop-limit equivalent size
    # => tx's vsize in mempool should also grow accordingly
    tx.vout[0].scriptPubKey = CScript([OP_RETURN, b'X'*(256+vsize_to_pad+1)])
    res = self.nodes[0].testmempoolaccept([tx.serialize().hex()])[0]
    assert_equal(res['allowed'], True)
    assert_equal(res['vsize'], sigop_equivalent_vsize+1)
    # decrease the tx's vsize to be right below the sigop-limit equivalent size
    # => tx's vsize in mempool should stick at the sigop-limit equivalent
    # bytes level, as it is higher than the tx's serialized vsize
    # (the maximum of both is taken)
    tx.vout[0].scriptPubKey = CScript([OP_RETURN, b'X'*(256+vsize_to_pad-1)])
    res = self.nodes[0].testmempoolaccept([tx.serialize().hex()])[0]
    assert_equal(res['allowed'], True)
    assert_equal(res['vsize'], sigop_equivalent_vsize)
    # check that the ancestor and descendant size calculations in the mempool
    # also use the same max(sigop_equivalent_vsize, serialized_vsize) logic
    # (to keep it simple, we only test the case here where the sigop vsize
    # is much larger than the serialized vsize, i.e. we create a small child
    # tx by getting rid of the large padding output)
    tx.vout[0].scriptPubKey = CScript([OP_RETURN, b'test123'])
    assert_greater_than(sigop_equivalent_vsize, tx.get_vsize())
    self.nodes[0].sendrawtransaction(hexstring=tx.serialize().hex(), maxburnamount='1.0')
    # fetch parent tx, which doesn't contain any sigops
    parent_txid = tx.vin[0].prevout.hash.to_bytes(32, 'big').hex()
    parent_tx = tx_from_hex(self.nodes[0].getrawtransaction(txid=parent_txid))
    entry_child = self.nodes[0].getmempoolentry(tx.rehash())
    assert_equal(entry_child['descendantcount'], 1)
    assert_equal(entry_child['descendantsize'], sigop_equivalent_vsize)
    assert_equal(entry_child['ancestorcount'], 2)
    assert_equal(entry_child['ancestorsize'], sigop_equivalent_vsize + parent_tx.get_vsize())
    entry_parent = self.nodes[0].getmempoolentry(parent_tx.rehash())
    assert_equal(entry_parent['ancestorcount'], 1)
    assert_equal(entry_parent['ancestorsize'], parent_tx.get_vsize())
    assert_equal(entry_parent['descendantcount'], 2)
    assert_equal(entry_parent['descendantsize'], parent_tx.get_vsize() + sigop_equivalent_vsize)